mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-03-14 11:43:50 +01:00
Merge branch 'master' into dependabot/pip/apscheduler-gte-3.10.4-and-lt-3.12.0
# Conflicts: # telegram/ext/_jobqueue.py # tests/auxil/bot_method_checks.py # tests/conftest.py
This commit is contained in:
commit
6e37a386f3
93 changed files with 2543 additions and 2031 deletions
9
.github/workflows/dependabot-prs.yml
vendored
9
.github/workflows/dependabot-prs.yml
vendored
|
@ -16,14 +16,15 @@ jobs:
|
|||
|
||||
- name: Fetch Dependabot metadata
|
||||
id: dependabot-metadata
|
||||
uses: dependabot/fetch-metadata@v2.2.0
|
||||
uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Update Version Number in Other Files
|
||||
uses: jacobtomlinson/gha-find-replace@v3
|
||||
uses: jacobtomlinson/gha-find-replace@f1069b438f125e5395d84d1c6fd3b559a7880cb5 # v3
|
||||
with:
|
||||
find: ${{ steps.dependabot-metadata.outputs.previous-version }}
|
||||
replace: ${{ steps.dependabot-metadata.outputs.new-version }}
|
||||
|
@ -31,7 +32,7 @@ jobs:
|
|||
exclude: CHANGES.rst
|
||||
|
||||
- name: Commit & Push Changes to PR
|
||||
uses: EndBug/add-and-commit@v9.1.4
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||
with:
|
||||
message: 'Update version number in other files'
|
||||
committer_name: GitHub Actions
|
||||
|
|
6
.github/workflows/docs-linkcheck.yml
vendored
6
.github/workflows/docs-linkcheck.yml
vendored
|
@ -17,9 +17,11 @@ jobs:
|
|||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
|
|
8
.github/workflows/docs.yml
vendored
8
.github/workflows/docs.yml
vendored
|
@ -18,9 +18,11 @@ jobs:
|
|||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
@ -34,7 +36,7 @@ jobs:
|
|||
- name: Build docs
|
||||
run: sphinx-build docs/source docs/build/html -W --keep-going -j auto
|
||||
- name: Upload docs
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: HTML Docs
|
||||
retention-days: 7
|
||||
|
|
31
.github/workflows/gha_security.yml
vendored
Normal file
31
.github/workflows/gha_security.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
name: GitHub Actions Security Analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
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@38f3f104447c67c051c4a08e39b64a148898af3a # v4.2.0
|
||||
- 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@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
2
.github/workflows/labelling.yml
vendored
2
.github/workflows/labelling.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
|||
pull-requests: write # for srvaroa/labeler to add labels in PR
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: srvaroa/labeler@v1.12.0
|
||||
- uses: srvaroa/labeler@fe4b1c73bb8abf2f14a44a6912a8b4fee835d631 # v1.12.0
|
||||
# Config file at .github/labeler.yml
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
|
@ -8,7 +8,7 @@ jobs:
|
|||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5.0.1
|
||||
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '7'
|
||||
|
|
24
.github/workflows/release_pypi.yml
vendored
24
.github/workflows/release_pypi.yml
vendored
|
@ -12,9 +12,11 @@ jobs:
|
|||
TAG: ${{ steps.get_tag.outputs.TAG }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install pypa/build
|
||||
|
@ -23,7 +25,7 @@ jobs:
|
|||
- name: Build a binary wheel and a source tarball
|
||||
run: python3 -m build
|
||||
- name: Store the distribution packages
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
@ -47,12 +49,12 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
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@release/v1
|
||||
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
|
||||
compute-signatures:
|
||||
name: Compute SHA1 Sums and Sign with Sigstore
|
||||
|
@ -65,7 +67,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
@ -77,13 +79,13 @@ jobs:
|
|||
sha1sum $file > $file.sha1
|
||||
done
|
||||
- name: Sign the dists with Sigstore
|
||||
uses: sigstore/gh-action-sigstore-python@v3.0.0
|
||||
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@v4
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
|
@ -101,7 +103,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
|
@ -113,7 +115,7 @@ jobs:
|
|||
# we don't define it through this workflow.
|
||||
run: >-
|
||||
gh release create
|
||||
'${{ env.TAG }}'
|
||||
"$TAG"
|
||||
--repo '${{ github.repository }}'
|
||||
--generate-notes
|
||||
- name: Upload artifact signatures to GitHub Release
|
||||
|
@ -125,5 +127,5 @@ jobs:
|
|||
# sigstore-produced signatures and certificates.
|
||||
run: >-
|
||||
gh release upload
|
||||
'${{ env.TAG }}' dist/**
|
||||
"$TAG" dist/**
|
||||
--repo '${{ github.repository }}'
|
||||
|
|
24
.github/workflows/release_test_pypi.yml
vendored
24
.github/workflows/release_test_pypi.yml
vendored
|
@ -12,9 +12,11 @@ jobs:
|
|||
TAG: ${{ steps.get_tag.outputs.TAG }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install pypa/build
|
||||
|
@ -23,7 +25,7 @@ jobs:
|
|||
- name: Build a binary wheel and a source tarball
|
||||
run: python3 -m build
|
||||
- name: Store the distribution packages
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
@ -47,12 +49,12 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
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@release/v1
|
||||
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
|
||||
|
@ -67,7 +69,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
@ -79,13 +81,13 @@ jobs:
|
|||
sha1sum $file > $file.sha1
|
||||
done
|
||||
- name: Sign the dists with Sigstore
|
||||
uses: sigstore/gh-action-sigstore-python@v3.0.0
|
||||
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@v4
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
|
@ -103,7 +105,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: python-package-distributions-and-signatures
|
||||
path: dist/
|
||||
|
@ -115,7 +117,7 @@ jobs:
|
|||
# we don't define it through this workflow.
|
||||
run: >-
|
||||
gh release create
|
||||
'${{ env.TAG }}'
|
||||
"$TAG"
|
||||
--repo '${{ github.repository }}'
|
||||
--generate-notes
|
||||
--draft
|
||||
|
@ -128,5 +130,5 @@ jobs:
|
|||
# sigstore-produced signatures and certificates.
|
||||
run: >-
|
||||
gh release upload
|
||||
'${{ env.TAG }}' dist/**
|
||||
"$TAG" dist/**
|
||||
--repo '${{ github.repository }}'
|
||||
|
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
|||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
||||
with:
|
||||
# PRs never get stale
|
||||
days-before-stale: 3
|
||||
|
|
8
.github/workflows/test_official.yml
vendored
8
.github/workflows/test_official.yml
vendored
|
@ -21,9 +21,11 @@ jobs:
|
|||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
|
@ -41,7 +43,7 @@ jobs:
|
|||
|
||||
- name: Test Summary
|
||||
id: test_summary
|
||||
uses: test-summary/action@v2.4
|
||||
uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4
|
||||
if: always() # always run, even if tests fail
|
||||
with:
|
||||
paths: .test_report_official.xml
|
||||
|
|
2
.github/workflows/type_completeness.yml
vendored
2
.github/workflows/type_completeness.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
name: test-type-completeness
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: Bibo-Joshi/pyright-type-completeness@1.0.1
|
||||
- uses: Bibo-Joshi/pyright-type-completeness@c85a67ff3c66f51dcbb2d06bfcf4fe83a57d69cc # v1.0.1
|
||||
with:
|
||||
package-name: telegram
|
||||
python-version: 3.12
|
||||
|
|
|
@ -9,14 +9,14 @@ jobs:
|
|||
name: test-type-completeness
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: Bibo-Joshi/pyright-type-completeness@1.0.1
|
||||
- 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@v1
|
||||
uses: jannekem/run-python-script-action@bbfca66c612a28f3eeca0ae40e1f810265e2ea68 # v1.7
|
||||
env:
|
||||
TYPE_COMPLETENESS: ${{ steps.pyright-type-completeness.outputs.base-completeness-score }}
|
||||
with:
|
||||
|
|
12
.github/workflows/unit_tests.yml
vendored
12
.github/workflows/unit_tests.yml
vendored
|
@ -24,9 +24,11 @@ jobs:
|
|||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
@ -79,7 +81,7 @@ jobs:
|
|||
|
||||
- name: Test Summary
|
||||
id: test_summary
|
||||
uses: test-summary/action@v2.4
|
||||
uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4
|
||||
if: always() # always run, even if tests fail
|
||||
with:
|
||||
paths: |
|
||||
|
@ -87,14 +89,14 @@ jobs:
|
|||
.test_report_optionals_junit.xml
|
||||
|
||||
- name: Submit coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e # v5.1.1
|
||||
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@v1
|
||||
uses: codecov/test-results-action@9739113ad922ea0a9abb4b2c0f8bf6a4aa8ef820 # v1.0.1
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
files: .test_report_no_optionals_junit.xml,.test_report_optionals_junit.xml
|
||||
|
|
|
@ -31,6 +31,7 @@ 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>`_
|
||||
|
@ -58,12 +59,14 @@ 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>`_
|
||||
|
@ -72,6 +75,7 @@ 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>`_
|
||||
|
@ -87,6 +91,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
|||
- `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>`_
|
||||
|
|
27
CHANGES.rst
27
CHANGES.rst
|
@ -4,6 +4,33 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
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*
|
||||
|
|
|
@ -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
|
||||
import datetime as dtm
|
||||
from enum import Enum
|
||||
|
||||
from docutils.nodes import Element
|
||||
|
@ -75,7 +75,7 @@ class TGConstXRefRole(PyXRefRole):
|
|||
):
|
||||
return str(value), target
|
||||
if (
|
||||
isinstance(value, datetime.datetime)
|
||||
isinstance(value, dtm.datetime)
|
||||
and value == telegram.constants.ZERO_DATE
|
||||
and target in ("telegram.constants.ZERO_DATE",)
|
||||
):
|
||||
|
|
|
@ -179,8 +179,8 @@ markers = [
|
|||
"req",
|
||||
]
|
||||
asyncio_mode = "auto"
|
||||
log_format = "%(funcName)s - Line %(lineno)d - %(message)s"
|
||||
# log_level = "DEBUG" # uncomment to see DEBUG logs
|
||||
log_cli_format = "%(funcName)s - Line %(lineno)d - %(message)s"
|
||||
# log_cli_level = "DEBUG" # uncomment to see DEBUG logs
|
||||
|
||||
# MYPY:
|
||||
[tool.mypy]
|
||||
|
|
|
@ -271,6 +271,17 @@ __all__ = (
|
|||
"warnings",
|
||||
)
|
||||
|
||||
from telegram._payment.stars.startransactions import StarTransaction, StarTransactions
|
||||
from telegram._payment.stars.transactionpartner import (
|
||||
TransactionPartner,
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
TransactionPartnerTelegramApi,
|
||||
TransactionPartnerUser,
|
||||
)
|
||||
|
||||
from . import _version, constants, error, helpers, request, warnings
|
||||
from ._birthdate import Birthdate
|
||||
from ._bot import Bot
|
||||
|
@ -470,21 +481,12 @@ from ._payment.refundedpayment import RefundedPayment
|
|||
from ._payment.shippingaddress import ShippingAddress
|
||||
from ._payment.shippingoption import ShippingOption
|
||||
from ._payment.shippingquery import ShippingQuery
|
||||
from ._payment.stars import (
|
||||
AffiliateInfo,
|
||||
from ._payment.stars.affiliateinfo import AffiliateInfo
|
||||
from ._payment.stars.revenuewithdrawalstate import (
|
||||
RevenueWithdrawalState,
|
||||
RevenueWithdrawalStateFailed,
|
||||
RevenueWithdrawalStatePending,
|
||||
RevenueWithdrawalStateSucceeded,
|
||||
StarTransaction,
|
||||
StarTransactions,
|
||||
TransactionPartner,
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
TransactionPartnerTelegramApi,
|
||||
TransactionPartnerUser,
|
||||
)
|
||||
from ._payment.successfulpayment import SuccessfulPayment
|
||||
from ._poll import InputPollOption, Poll, PollAnswer, PollOption
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Birthday."""
|
||||
from datetime import date
|
||||
import datetime as dtm
|
||||
from typing import Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
|
@ -70,7 +70,7 @@ class Birthdate(TelegramObject):
|
|||
|
||||
self._freeze()
|
||||
|
||||
def to_date(self, year: Optional[int] = None) -> date:
|
||||
def to_date(self, year: Optional[int] = None) -> dtm.date:
|
||||
"""Return the birthdate as a date object.
|
||||
|
||||
.. versionchanged:: 21.2
|
||||
|
@ -89,4 +89,4 @@ class Birthdate(TelegramObject):
|
|||
"The `year` argument is required if the `year` attribute was not present."
|
||||
)
|
||||
|
||||
return date(year or self.year, self.month, self.day) # type: ignore[arg-type]
|
||||
return dtm.date(year or self.year, self.month, self.day) # type: ignore[arg-type]
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
import asyncio
|
||||
import contextlib
|
||||
import copy
|
||||
import datetime as dtm
|
||||
import pickle
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime, timedelta
|
||||
from types import TracebackType
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
|
@ -81,7 +81,7 @@ from telegram._inline.preparedinlinemessage import PreparedInlineMessage
|
|||
from telegram._menubutton import MenuButton
|
||||
from telegram._message import Message
|
||||
from telegram._messageid import MessageId
|
||||
from telegram._payment.stars import StarTransactions
|
||||
from telegram._payment.stars.startransactions import StarTransactions
|
||||
from telegram._poll import InputPollOption, Poll
|
||||
from telegram._reaction import ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||
from telegram._reply import ReplyParameters
|
||||
|
@ -3815,7 +3815,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
|||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: int,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
until_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
revoke_messages: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -5451,7 +5451,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
|||
chat_id: Union[str, int],
|
||||
user_id: int,
|
||||
permissions: ChatPermissions,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
until_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
use_independent_chat_permissions: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -5788,7 +5788,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
|||
async def create_chat_invite_link(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
expire_date: Optional[Union[int, datetime]] = None,
|
||||
expire_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
member_limit: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
creates_join_request: Optional[bool] = None,
|
||||
|
@ -5864,7 +5864,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
|||
self,
|
||||
chat_id: Union[str, int],
|
||||
invite_link: Union[str, "ChatInviteLink"],
|
||||
expire_date: Optional[Union[int, datetime]] = None,
|
||||
expire_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
member_limit: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
creates_join_request: Optional[bool] = None,
|
||||
|
@ -6233,7 +6233,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
|||
self,
|
||||
user_id: int,
|
||||
emoji_status_custom_emoji_id: Optional[str] = None,
|
||||
emoji_status_expiration_date: Optional[Union[int, datetime]] = None,
|
||||
emoji_status_expiration_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -6622,7 +6622,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
|
||||
async def set_sticker_position_in_set(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
position: int,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -6634,7 +6634,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
"""Use this method to move a sticker in a set created by the bot to a specific position.
|
||||
|
||||
Args:
|
||||
sticker (:obj:`str`): File identifier of the sticker.
|
||||
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
|
||||
the sticker object.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
Accepts also :class:`telegram.Sticker` instances.
|
||||
position (:obj:`int`): New sticker position in the set, zero-based.
|
||||
|
||||
Returns:
|
||||
|
@ -6644,7 +6648,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
:class:`telegram.error.TelegramError`
|
||||
|
||||
"""
|
||||
data: JSONDict = {"sticker": sticker, "position": position}
|
||||
data: JSONDict = {
|
||||
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
|
||||
"position": position,
|
||||
}
|
||||
return await self._post(
|
||||
"setStickerPositionInSet",
|
||||
data,
|
||||
|
@ -6749,7 +6756,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
|
||||
async def delete_sticker_from_set(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -6760,7 +6767,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
"""Use this method to delete a sticker from a set created by the bot.
|
||||
|
||||
Args:
|
||||
sticker (:obj:`str`): File identifier of the sticker.
|
||||
sticker (:obj:`str` | :class:`telegram.Sticker`): File identifier of the sticker or
|
||||
the sticker object.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
Accepts also :class:`telegram.Sticker` instances.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
@ -6769,7 +6780,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
:class:`telegram.error.TelegramError`
|
||||
|
||||
"""
|
||||
data: JSONDict = {"sticker": sticker}
|
||||
data: JSONDict = {"sticker": sticker if isinstance(sticker, str) else sticker.file_id}
|
||||
return await self._post(
|
||||
"deleteStickerFromSet",
|
||||
data,
|
||||
|
@ -6937,7 +6948,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
|
||||
async def set_sticker_emoji_list(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
emoji_list: Sequence[str],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -6953,7 +6964,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
.. versionadded:: 20.2
|
||||
|
||||
Args:
|
||||
sticker (:obj:`str`): File identifier of the sticker.
|
||||
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
|
||||
the sticker object.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
Accepts also :class:`telegram.Sticker` instances.
|
||||
emoji_list (Sequence[:obj:`str`]): A sequence of
|
||||
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI`-
|
||||
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with
|
||||
|
@ -6965,7 +6980,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {"sticker": sticker, "emoji_list": emoji_list}
|
||||
data: JSONDict = {
|
||||
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
|
||||
"emoji_list": emoji_list,
|
||||
}
|
||||
return await self._post(
|
||||
"setStickerEmojiList",
|
||||
data,
|
||||
|
@ -6978,7 +6996,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
|
||||
async def set_sticker_keywords(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
keywords: Optional[Sequence[str]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -6994,7 +7012,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
.. versionadded:: 20.2
|
||||
|
||||
Args:
|
||||
sticker (:obj:`str`): File identifier of the sticker.
|
||||
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
|
||||
the sticker object.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
Accepts also :class:`telegram.Sticker` instances.
|
||||
keywords (Sequence[:obj:`str`]): A sequence of
|
||||
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
|
||||
for the sticker with total length up to
|
||||
|
@ -7006,7 +7028,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {"sticker": sticker, "keywords": keywords}
|
||||
data: JSONDict = {
|
||||
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
|
||||
"keywords": keywords,
|
||||
}
|
||||
return await self._post(
|
||||
"setStickerKeywords",
|
||||
data,
|
||||
|
@ -7019,7 +7044,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
|
||||
async def set_sticker_mask_position(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
mask_position: Optional[MaskPosition] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -7035,7 +7060,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
.. versionadded:: 20.2
|
||||
|
||||
Args:
|
||||
sticker (:obj:`str`): File identifier of the sticker.
|
||||
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
|
||||
the sticker object.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
Accepts also :class:`telegram.Sticker` instances.
|
||||
mask_position (:class:`telegram.MaskPosition`, optional): A object with the position
|
||||
where the mask should be placed on faces. Omit the parameter to remove the mask
|
||||
position.
|
||||
|
@ -7046,7 +7075,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {"sticker": sticker, "mask_position": mask_position}
|
||||
data: JSONDict = {
|
||||
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
|
||||
"mask_position": mask_position,
|
||||
}
|
||||
return await self._post(
|
||||
"setStickerMaskPosition",
|
||||
data,
|
||||
|
@ -7159,7 +7191,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
explanation: Optional[str] = None,
|
||||
explanation_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
open_period: Optional[int] = None,
|
||||
close_date: Optional[Union[int, datetime]] = None,
|
||||
close_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
explanation_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
@ -8125,7 +8157,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
send_phone_number_to_provider: Optional[bool] = None,
|
||||
send_email_to_provider: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
subscription_period: Optional[Union[int, timedelta]] = None,
|
||||
subscription_period: Optional[Union[int, dtm.timedelta]] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -8248,7 +8280,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
"send_email_to_provider": send_email_to_provider,
|
||||
"subscription_period": (
|
||||
subscription_period.total_seconds()
|
||||
if isinstance(subscription_period, timedelta)
|
||||
if isinstance(subscription_period, dtm.timedelta)
|
||||
else subscription_period
|
||||
),
|
||||
"business_connection_id": business_connection_id,
|
||||
|
@ -9248,7 +9280,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
self,
|
||||
user_id: int,
|
||||
name: str,
|
||||
old_sticker: str,
|
||||
old_sticker: Union[str, "Sticker"],
|
||||
sticker: "InputSticker",
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -9266,7 +9298,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
Args:
|
||||
user_id (:obj:`int`): User identifier of the sticker set owner.
|
||||
name (:obj:`str`): Sticker set name.
|
||||
old_sticker (:obj:`str`): File identifier of the replaced sticker.
|
||||
old_sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the replaced
|
||||
sticker or the sticker object itself.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
Accepts also :class:`telegram.Sticker` instances.
|
||||
sticker (:class:`telegram.InputSticker`): An object with information about the added
|
||||
sticker. If exactly the same sticker had already been added to the set, then the
|
||||
set remains unchanged.
|
||||
|
@ -9280,7 +9316,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
data: JSONDict = {
|
||||
"user_id": user_id,
|
||||
"name": name,
|
||||
"old_sticker": old_sticker,
|
||||
"old_sticker": old_sticker if isinstance(old_sticker, str) else old_sticker.file_id,
|
||||
"sticker": sticker,
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/]
|
||||
"""This module contains the Telegram Business related classes."""
|
||||
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._chat import Chat
|
||||
|
@ -81,7 +80,7 @@ class BusinessConnection(TelegramObject):
|
|||
id: str,
|
||||
user: "User",
|
||||
user_chat_id: int,
|
||||
date: datetime,
|
||||
date: dtm.datetime,
|
||||
can_reply: bool,
|
||||
is_enabled: bool,
|
||||
*,
|
||||
|
@ -91,7 +90,7 @@ class BusinessConnection(TelegramObject):
|
|||
self.id: str = id
|
||||
self.user: User = user
|
||||
self.user_chat_id: int = user_chat_id
|
||||
self.date: datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
self.can_reply: bool = can_reply
|
||||
self.is_enabled: bool = is_enabled
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Chat."""
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from html import escape
|
||||
from typing import TYPE_CHECKING, Final, Optional, Union
|
||||
|
||||
|
@ -384,7 +384,7 @@ class _ChatBase(TelegramObject):
|
|||
self,
|
||||
user_id: int,
|
||||
revoke_messages: Optional[bool] = None,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
until_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -656,7 +656,7 @@ class _ChatBase(TelegramObject):
|
|||
self,
|
||||
user_id: int,
|
||||
permissions: ChatPermissions,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
until_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
use_independent_chat_permissions: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -2116,7 +2116,7 @@ class _ChatBase(TelegramObject):
|
|||
explanation: Optional[str] = None,
|
||||
explanation_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
open_period: Optional[int] = None,
|
||||
close_date: Optional[Union[int, datetime]] = None,
|
||||
close_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
explanation_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
@ -2588,7 +2588,7 @@ class _ChatBase(TelegramObject):
|
|||
|
||||
async def create_invite_link(
|
||||
self,
|
||||
expire_date: Optional[Union[int, datetime]] = None,
|
||||
expire_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
member_limit: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
creates_join_request: Optional[bool] = None,
|
||||
|
@ -2632,7 +2632,7 @@ class _ChatBase(TelegramObject):
|
|||
async def edit_invite_link(
|
||||
self,
|
||||
invite_link: Union[str, "ChatInviteLink"],
|
||||
expire_date: Optional[Union[int, datetime]] = None,
|
||||
expire_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
member_limit: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
creates_join_request: Optional[bool] = None,
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the classes that represent Telegram ChatBoosts."""
|
||||
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
|
@ -274,8 +273,8 @@ class ChatBoost(TelegramObject):
|
|||
def __init__(
|
||||
self,
|
||||
boost_id: str,
|
||||
add_date: datetime,
|
||||
expiration_date: datetime,
|
||||
add_date: dtm.datetime,
|
||||
expiration_date: dtm.datetime,
|
||||
source: ChatBoostSource,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
|
@ -283,8 +282,8 @@ class ChatBoost(TelegramObject):
|
|||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
self.boost_id: str = boost_id
|
||||
self.add_date: datetime = add_date
|
||||
self.expiration_date: datetime = expiration_date
|
||||
self.add_date: dtm.datetime = add_date
|
||||
self.expiration_date: dtm.datetime = expiration_date
|
||||
self.source: ChatBoostSource = source
|
||||
|
||||
self._id_attrs = (self.boost_id, self.add_date, self.expiration_date, self.source)
|
||||
|
@ -386,7 +385,7 @@ class ChatBoostRemoved(TelegramObject):
|
|||
self,
|
||||
chat: Chat,
|
||||
boost_id: str,
|
||||
remove_date: datetime,
|
||||
remove_date: dtm.datetime,
|
||||
source: ChatBoostSource,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
|
@ -395,7 +394,7 @@ class ChatBoostRemoved(TelegramObject):
|
|||
|
||||
self.chat: Chat = chat
|
||||
self.boost_id: str = boost_id
|
||||
self.remove_date: datetime = remove_date
|
||||
self.remove_date: dtm.datetime = remove_date
|
||||
self.source: ChatBoostSource = source
|
||||
|
||||
self._id_attrs = (self.chat, self.boost_id, self.remove_date, self.source)
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram ChatFullInfo."""
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._birthdate import Birthdate
|
||||
|
@ -422,7 +422,7 @@ class ChatFullInfo(_ChatBase):
|
|||
profile_accent_color_id: Optional[int] = None,
|
||||
profile_background_custom_emoji_id: Optional[str] = None,
|
||||
emoji_status_custom_emoji_id: Optional[str] = None,
|
||||
emoji_status_expiration_date: Optional[datetime] = None,
|
||||
emoji_status_expiration_date: Optional[dtm.datetime] = None,
|
||||
bio: Optional[str] = None,
|
||||
has_private_forwards: Optional[bool] = None,
|
||||
has_restricted_voice_and_video_messages: Optional[bool] = None,
|
||||
|
@ -486,7 +486,9 @@ class ChatFullInfo(_ChatBase):
|
|||
)
|
||||
self.active_usernames: tuple[str, ...] = parse_sequence_arg(active_usernames)
|
||||
self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id
|
||||
self.emoji_status_expiration_date: Optional[datetime] = emoji_status_expiration_date
|
||||
self.emoji_status_expiration_date: Optional[dtm.datetime] = (
|
||||
emoji_status_expiration_date
|
||||
)
|
||||
self.has_aggressive_anti_spam_enabled: Optional[bool] = (
|
||||
has_aggressive_anti_spam_enabled
|
||||
)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents an invite link for a chat."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
|
@ -139,7 +139,7 @@ class ChatInviteLink(TelegramObject):
|
|||
creates_join_request: bool,
|
||||
is_primary: bool,
|
||||
is_revoked: bool,
|
||||
expire_date: Optional[datetime.datetime] = None,
|
||||
expire_date: Optional[dtm.datetime] = None,
|
||||
member_limit: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
pending_join_request_count: Optional[int] = None,
|
||||
|
@ -157,7 +157,7 @@ class ChatInviteLink(TelegramObject):
|
|||
self.is_revoked: bool = is_revoked
|
||||
|
||||
# Optionals
|
||||
self.expire_date: Optional[datetime.datetime] = expire_date
|
||||
self.expire_date: Optional[dtm.datetime] = expire_date
|
||||
self.member_limit: Optional[int] = member_limit
|
||||
self.name: Optional[str] = name
|
||||
self.pending_join_request_count: Optional[int] = (
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram ChatJoinRequest."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._chat import Chat
|
||||
|
@ -106,7 +106,7 @@ class ChatJoinRequest(TelegramObject):
|
|||
self,
|
||||
chat: Chat,
|
||||
from_user: User,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
user_chat_id: int,
|
||||
bio: Optional[str] = None,
|
||||
invite_link: Optional[ChatInviteLink] = None,
|
||||
|
@ -117,7 +117,7 @@ class ChatJoinRequest(TelegramObject):
|
|||
# Required
|
||||
self.chat: Chat = chat
|
||||
self.from_user: User = from_user
|
||||
self.date: datetime.datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
self.user_chat_id: int = user_chat_id
|
||||
|
||||
# Optionals
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram ChatMember."""
|
||||
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
|
@ -413,13 +413,13 @@ class ChatMemberMember(ChatMember):
|
|||
def __init__(
|
||||
self,
|
||||
user: User,
|
||||
until_date: Optional[datetime.datetime] = None,
|
||||
until_date: Optional[dtm.datetime] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.until_date: Optional[datetime.datetime] = until_date
|
||||
self.until_date: Optional[dtm.datetime] = until_date
|
||||
|
||||
|
||||
class ChatMemberRestricted(ChatMember):
|
||||
|
@ -566,7 +566,7 @@ class ChatMemberRestricted(ChatMember):
|
|||
can_send_other_messages: bool,
|
||||
can_add_web_page_previews: bool,
|
||||
can_manage_topics: bool,
|
||||
until_date: datetime.datetime,
|
||||
until_date: dtm.datetime,
|
||||
can_send_audios: bool,
|
||||
can_send_documents: bool,
|
||||
can_send_photos: bool,
|
||||
|
@ -587,7 +587,7 @@ class ChatMemberRestricted(ChatMember):
|
|||
self.can_send_other_messages: bool = can_send_other_messages
|
||||
self.can_add_web_page_previews: bool = can_add_web_page_previews
|
||||
self.can_manage_topics: bool = can_manage_topics
|
||||
self.until_date: datetime.datetime = until_date
|
||||
self.until_date: dtm.datetime = until_date
|
||||
self.can_send_audios: bool = can_send_audios
|
||||
self.can_send_documents: bool = can_send_documents
|
||||
self.can_send_photos: bool = can_send_photos
|
||||
|
@ -656,10 +656,10 @@ class ChatMemberBanned(ChatMember):
|
|||
def __init__(
|
||||
self,
|
||||
user: User,
|
||||
until_date: datetime.datetime,
|
||||
until_date: dtm.datetime,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.until_date: datetime.datetime = until_date
|
||||
self.until_date: dtm.datetime = until_date
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram ChatMemberUpdated."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from telegram._chat import Chat
|
||||
|
@ -108,7 +108,7 @@ class ChatMemberUpdated(TelegramObject):
|
|||
self,
|
||||
chat: Chat,
|
||||
from_user: User,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
old_chat_member: ChatMember,
|
||||
new_chat_member: ChatMember,
|
||||
invite_link: Optional[ChatInviteLink] = None,
|
||||
|
@ -121,7 +121,7 @@ class ChatMemberUpdated(TelegramObject):
|
|||
# Required
|
||||
self.chat: Chat = chat
|
||||
self.from_user: User = from_user
|
||||
self.date: datetime.datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
self.old_chat_member: ChatMember = old_chat_member
|
||||
self.new_chat_member: ChatMember = new_chat_member
|
||||
self.via_chat_folder_invite_link: Optional[bool] = via_chat_folder_invite_link
|
||||
|
@ -179,9 +179,7 @@ class ChatMemberUpdated(TelegramObject):
|
|||
self,
|
||||
) -> dict[
|
||||
str,
|
||||
tuple[
|
||||
Union[str, bool, datetime.datetime, User], Union[str, bool, datetime.datetime, User]
|
||||
],
|
||||
tuple[Union[str, bool, dtm.datetime, User], Union[str, bool, dtm.datetime, User]],
|
||||
]:
|
||||
"""Computes the difference between :attr:`old_chat_member` and :attr:`new_chat_member`.
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an objects that are related to Telegram giveaways."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
|
@ -105,7 +105,7 @@ class Giveaway(TelegramObject):
|
|||
def __init__(
|
||||
self,
|
||||
chats: Sequence[Chat],
|
||||
winners_selection_date: datetime.datetime,
|
||||
winners_selection_date: dtm.datetime,
|
||||
winner_count: int,
|
||||
only_new_members: Optional[bool] = None,
|
||||
has_public_winners: Optional[bool] = None,
|
||||
|
@ -119,7 +119,7 @@ class Giveaway(TelegramObject):
|
|||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
self.chats: tuple[Chat, ...] = tuple(chats)
|
||||
self.winners_selection_date: datetime.datetime = winners_selection_date
|
||||
self.winners_selection_date: dtm.datetime = winners_selection_date
|
||||
self.winner_count: int = winner_count
|
||||
self.only_new_members: Optional[bool] = only_new_members
|
||||
self.has_public_winners: Optional[bool] = has_public_winners
|
||||
|
@ -260,7 +260,7 @@ class GiveawayWinners(TelegramObject):
|
|||
self,
|
||||
chat: Chat,
|
||||
giveaway_message_id: int,
|
||||
winners_selection_date: datetime.datetime,
|
||||
winners_selection_date: dtm.datetime,
|
||||
winner_count: int,
|
||||
winners: Sequence[User],
|
||||
additional_chat_count: Optional[int] = None,
|
||||
|
@ -277,7 +277,7 @@ class GiveawayWinners(TelegramObject):
|
|||
|
||||
self.chat: Chat = chat
|
||||
self.giveaway_message_id: int = giveaway_message_id
|
||||
self.winners_selection_date: datetime.datetime = winners_selection_date
|
||||
self.winners_selection_date: dtm.datetime = winners_selection_date
|
||||
self.winner_count: int = winner_count
|
||||
self.winners: tuple[User, ...] = tuple(winners)
|
||||
self.additional_chat_count: Optional[int] = additional_chat_count
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Message."""
|
||||
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from html import escape
|
||||
|
@ -158,14 +158,14 @@ class MaybeInaccessibleMessage(TelegramObject):
|
|||
self,
|
||||
chat: Chat,
|
||||
message_id: int,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.chat: Chat = chat
|
||||
self.message_id: int = message_id
|
||||
self.date: datetime.datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
|
||||
self._id_attrs = (self.message_id, self.chat)
|
||||
|
||||
|
@ -1024,11 +1024,11 @@ class Message(MaybeInaccessibleMessage):
|
|||
def __init__(
|
||||
self,
|
||||
message_id: int,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
chat: Chat,
|
||||
from_user: Optional[User] = None,
|
||||
reply_to_message: Optional["Message"] = None,
|
||||
edit_date: Optional[datetime.datetime] = None,
|
||||
edit_date: Optional[dtm.datetime] = None,
|
||||
text: Optional[str] = None,
|
||||
entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
|
@ -1119,11 +1119,11 @@ class Message(MaybeInaccessibleMessage):
|
|||
# Optionals
|
||||
self.from_user: Optional[User] = from_user
|
||||
self.sender_chat: Optional[Chat] = sender_chat
|
||||
self.date: datetime.datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
self.chat: Chat = chat
|
||||
self.is_automatic_forward: Optional[bool] = is_automatic_forward
|
||||
self.reply_to_message: Optional[Message] = reply_to_message
|
||||
self.edit_date: Optional[datetime.datetime] = edit_date
|
||||
self.edit_date: Optional[dtm.datetime] = edit_date
|
||||
self.has_protected_content: Optional[bool] = has_protected_content
|
||||
self.text: Optional[str] = text
|
||||
self.entities: tuple[MessageEntity, ...] = parse_sequence_arg(entities)
|
||||
|
@ -3077,7 +3077,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
explanation: Optional[str] = None,
|
||||
explanation_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
open_period: Optional[int] = None,
|
||||
close_date: Optional[Union[int, datetime.datetime]] = None,
|
||||
close_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
explanation_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: ODVInput[int] = DEFAULT_NONE,
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the classes that represent Telegram MessageOigin."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
|
@ -78,14 +78,14 @@ class MessageOrigin(TelegramObject):
|
|||
def __init__(
|
||||
self,
|
||||
type: str, # pylint: disable=W0622
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required by all subclasses
|
||||
self.type: str = enum.get_member(constants.MessageOriginType, type, type)
|
||||
self.date: datetime.datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
|
@ -152,7 +152,7 @@ class MessageOriginUser(MessageOrigin):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
sender_user: User,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
|
@ -186,7 +186,7 @@ class MessageOriginHiddenUser(MessageOrigin):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
sender_user_name: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
|
@ -227,7 +227,7 @@ class MessageOriginChat(MessageOrigin):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
sender_chat: Chat,
|
||||
author_signature: Optional[str] = None,
|
||||
*,
|
||||
|
@ -271,7 +271,7 @@ class MessageOriginChannel(MessageOrigin):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
date: datetime.datetime,
|
||||
date: dtm.datetime,
|
||||
chat: Chat,
|
||||
message_id: int,
|
||||
author_signature: Optional[str] = None,
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram MessageReaction Update."""
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._chat import Chat
|
||||
|
@ -70,7 +70,7 @@ class MessageReactionCountUpdated(TelegramObject):
|
|||
self,
|
||||
chat: Chat,
|
||||
message_id: int,
|
||||
date: datetime,
|
||||
date: dtm.datetime,
|
||||
reactions: Sequence[ReactionCount],
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
|
@ -79,7 +79,7 @@ class MessageReactionCountUpdated(TelegramObject):
|
|||
# Required
|
||||
self.chat: Chat = chat
|
||||
self.message_id: int = message_id
|
||||
self.date: datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
self.reactions: tuple[ReactionCount, ...] = parse_sequence_arg(reactions)
|
||||
|
||||
self._id_attrs = (self.chat, self.message_id, self.date, self.reactions)
|
||||
|
@ -157,7 +157,7 @@ class MessageReactionUpdated(TelegramObject):
|
|||
self,
|
||||
chat: Chat,
|
||||
message_id: int,
|
||||
date: datetime,
|
||||
date: dtm.datetime,
|
||||
old_reaction: Sequence[ReactionType],
|
||||
new_reaction: Sequence[ReactionType],
|
||||
user: Optional[User] = None,
|
||||
|
@ -169,7 +169,7 @@ class MessageReactionUpdated(TelegramObject):
|
|||
# Required
|
||||
self.chat: Chat = chat
|
||||
self.message_id: int = message_id
|
||||
self.date: datetime = date
|
||||
self.date: dtm.datetime = date
|
||||
self.old_reaction: tuple[ReactionType, ...] = parse_sequence_arg(old_reaction)
|
||||
self.new_reaction: tuple[ReactionType, ...] = parse_sequence_arg(new_reaction)
|
||||
|
||||
|
|
|
@ -1,795 +0,0 @@
|
|||
#!/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/].
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes for Telegram Stars transactions."""
|
||||
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
from telegram._chat import Chat
|
||||
from telegram._gifts import Gift
|
||||
from telegram._paidmedia import PaidMedia
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class RevenueWithdrawalState(TelegramObject):
|
||||
"""This object escribes the state of a revenue withdrawal operation. Currently, it can be one
|
||||
of:
|
||||
|
||||
* :class:`telegram.RevenueWithdrawalStatePending`
|
||||
* :class:`telegram.RevenueWithdrawalStateSucceeded`
|
||||
* :class:`telegram.RevenueWithdrawalStateFailed`
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The type of the state.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state.
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
PENDING: Final[str] = constants.RevenueWithdrawalStateType.PENDING
|
||||
""":const:`telegram.constants.RevenueWithdrawalStateType.PENDING`"""
|
||||
SUCCEEDED: Final[str] = constants.RevenueWithdrawalStateType.SUCCEEDED
|
||||
""":const:`telegram.constants.RevenueWithdrawalStateType.SUCCEEDED`"""
|
||||
FAILED: Final[str] = constants.RevenueWithdrawalStateType.FAILED
|
||||
""":const:`telegram.constants.RevenueWithdrawalStateType.FAILED`"""
|
||||
|
||||
def __init__(self, type: str, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: str = enum.get_member(constants.RevenueWithdrawalStateType, type, type)
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["RevenueWithdrawalState"]:
|
||||
"""Converts JSON data to the appropriate :class:`RevenueWithdrawalState` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
Args:
|
||||
data (dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[RevenueWithdrawalState]] = {
|
||||
cls.PENDING: RevenueWithdrawalStatePending,
|
||||
cls.SUCCEEDED: RevenueWithdrawalStateSucceeded,
|
||||
cls.FAILED: RevenueWithdrawalStateFailed,
|
||||
}
|
||||
|
||||
if cls is RevenueWithdrawalState and data.get("type") in _class_mapping:
|
||||
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class RevenueWithdrawalStatePending(RevenueWithdrawalState):
|
||||
"""The withdrawal is in progress.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state, always
|
||||
:tg-const:`telegram.RevenueWithdrawalState.PENDING`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=RevenueWithdrawalState.PENDING, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState):
|
||||
"""The withdrawal succeeded.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`date` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
date (:obj:`datetime.datetime`): Date the withdrawal was completed as a datetime object.
|
||||
url (:obj:`str`): An HTTPS URL that can be used to see transaction details.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state, always
|
||||
:tg-const:`telegram.RevenueWithdrawalState.SUCCEEDED`.
|
||||
date (:obj:`datetime.datetime`): Date the withdrawal was completed as a datetime object.
|
||||
url (:obj:`str`): An HTTPS URL that can be used to see transaction details.
|
||||
"""
|
||||
|
||||
__slots__ = ("date", "url")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
date: dtm.datetime,
|
||||
url: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=RevenueWithdrawalState.SUCCEEDED, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.date: dtm.datetime = date
|
||||
self.url: str = url
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.date,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["RevenueWithdrawalStateSucceeded"]:
|
||||
"""See :meth:`telegram.RevenueWithdrawalState.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["date"] = from_timestamp(data.get("date", None), tzinfo=loc_tzinfo)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class RevenueWithdrawalStateFailed(RevenueWithdrawalState):
|
||||
"""The withdrawal failed and the transaction was refunded.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state, always
|
||||
:tg-const:`telegram.RevenueWithdrawalState.FAILED`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=RevenueWithdrawalState.FAILED, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class AffiliateInfo(TelegramObject):
|
||||
"""Contains information about the affiliate that received a commission via this transaction.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`affiliate_user`, :attr:`affiliate_chat`,
|
||||
:attr:`commission_per_mille`, :attr:`amount`, and :attr:`nanostar_amount` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
affiliate_user (:class:`telegram.User`, optional): The bot or the user that received an
|
||||
affiliate commission if it was received by a bot or a user
|
||||
affiliate_chat (:class:`telegram.Chat`, optional): The chat that received an affiliate
|
||||
commission if it was received by a chat
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the affiliate
|
||||
for each 1000 Telegram Stars received by the bot from referred users
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars received by the affiliate from the
|
||||
transaction, rounded to 0; can be negative for refunds
|
||||
nanostar_amount (:obj:`int`, optional): The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars received by the affiliate; from
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MIN_AMOUNT` to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`;
|
||||
can be negative for refunds
|
||||
|
||||
Attributes:
|
||||
affiliate_user (:class:`telegram.User`): Optional. The bot or the user that received an
|
||||
affiliate commission if it was received by a bot or a user
|
||||
affiliate_chat (:class:`telegram.Chat`): Optional. The chat that received an affiliate
|
||||
commission if it was received by a chat
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the affiliate
|
||||
for each 1000 Telegram Stars received by the bot from referred users
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars received by the affiliate from the
|
||||
transaction, rounded to 0; can be negative for refunds
|
||||
nanostar_amount (:obj:`int`): Optional. The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars received by the affiliate; from
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MIN_AMOUNT` to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`;
|
||||
can be negative for refunds
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"affiliate_chat",
|
||||
"affiliate_user",
|
||||
"amount",
|
||||
"commission_per_mille",
|
||||
"nanostar_amount",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
commission_per_mille: int,
|
||||
amount: int,
|
||||
affiliate_user: Optional["User"] = None,
|
||||
affiliate_chat: Optional["Chat"] = None,
|
||||
nanostar_amount: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.affiliate_user: Optional[User] = affiliate_user
|
||||
self.affiliate_chat: Optional[Chat] = affiliate_chat
|
||||
self.commission_per_mille: int = commission_per_mille
|
||||
self.amount: int = amount
|
||||
self.nanostar_amount: Optional[int] = nanostar_amount
|
||||
|
||||
self._id_attrs = (
|
||||
self.affiliate_user,
|
||||
self.affiliate_chat,
|
||||
self.commission_per_mille,
|
||||
self.amount,
|
||||
self.nanostar_amount,
|
||||
)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["AffiliateInfo"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["affiliate_user"] = User.de_json(data.get("affiliate_user"), bot)
|
||||
data["affiliate_chat"] = Chat.de_json(data.get("affiliate_chat"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class TransactionPartner(TelegramObject):
|
||||
"""This object describes the source of a transaction, or its recipient for outgoing
|
||||
transactions. Currently, it can be one of:
|
||||
|
||||
* :class:`TransactionPartnerUser`
|
||||
* :class:`TransactionPartnerAffiliateProgram`
|
||||
* :class:`TransactionPartnerFragment`
|
||||
* :class:`TransactionPartnerTelegramAds`
|
||||
* :class:`TransactionPartnerTelegramApi`
|
||||
* :class:`TransactionPartnerOther`
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The type of the transaction partner.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner.
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
AFFILIATE_PROGRAM: Final[str] = constants.TransactionPartnerType.AFFILIATE_PROGRAM
|
||||
""":const:`telegram.constants.TransactionPartnerType.AFFILIATE_PROGRAM`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
FRAGMENT: Final[str] = constants.TransactionPartnerType.FRAGMENT
|
||||
""":const:`telegram.constants.TransactionPartnerType.FRAGMENT`"""
|
||||
OTHER: Final[str] = constants.TransactionPartnerType.OTHER
|
||||
""":const:`telegram.constants.TransactionPartnerType.OTHER`"""
|
||||
TELEGRAM_ADS: Final[str] = constants.TransactionPartnerType.TELEGRAM_ADS
|
||||
""":const:`telegram.constants.TransactionPartnerType.TELEGRAM_ADS`"""
|
||||
TELEGRAM_API: Final[str] = constants.TransactionPartnerType.TELEGRAM_API
|
||||
""":const:`telegram.constants.TransactionPartnerType.TELEGRAM_API`"""
|
||||
USER: Final[str] = constants.TransactionPartnerType.USER
|
||||
""":const:`telegram.constants.TransactionPartnerType.USER`"""
|
||||
|
||||
def __init__(self, type: str, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: str = enum.get_member(constants.TransactionPartnerType, type, type)
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartner"]:
|
||||
"""Converts JSON data to the appropriate :class:`TransactionPartner` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
Args:
|
||||
data (dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
if not data and cls is TransactionPartner:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[TransactionPartner]] = {
|
||||
cls.AFFILIATE_PROGRAM: TransactionPartnerAffiliateProgram,
|
||||
cls.FRAGMENT: TransactionPartnerFragment,
|
||||
cls.USER: TransactionPartnerUser,
|
||||
cls.TELEGRAM_ADS: TransactionPartnerTelegramAds,
|
||||
cls.TELEGRAM_API: TransactionPartnerTelegramApi,
|
||||
cls.OTHER: TransactionPartnerOther,
|
||||
}
|
||||
|
||||
if cls is TransactionPartner and data.get("type") in _class_mapping:
|
||||
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class TransactionPartnerAffiliateProgram(TransactionPartner):
|
||||
"""Describes the affiliate program that issued the affiliate commission received via this
|
||||
transaction.
|
||||
|
||||
This object is comparable in terms of equality. Two objects of this class are considered equal,
|
||||
if their :attr:`commission_per_mille` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
sponsor_user (:class:`telegram.User`, optional): Information about the bot that sponsored
|
||||
the affiliate program
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the bot for
|
||||
each 1000 Telegram Stars received by the affiliate program sponsor from referred users.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.AFFILIATE_PROGRAM`.
|
||||
sponsor_user (:class:`telegram.User`): Optional. Information about the bot that sponsored
|
||||
the affiliate program
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the bot for
|
||||
each 1000 Telegram Stars received by the affiliate program sponsor from referred users.
|
||||
"""
|
||||
|
||||
__slots__ = ("commission_per_mille", "sponsor_user")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
commission_per_mille: int,
|
||||
sponsor_user: Optional["User"] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.AFFILIATE_PROGRAM, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.sponsor_user: Optional[User] = sponsor_user
|
||||
self.commission_per_mille: int = commission_per_mille
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.commission_per_mille,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerAffiliateProgram"]:
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["sponsor_user"] = User.de_json(data.get("sponsor_user"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class TransactionPartnerFragment(TransactionPartner):
|
||||
"""Describes a withdrawal transaction with Fragment.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
withdrawal_state (:class:`telegram.RevenueWithdrawalState`, optional): State of the
|
||||
transaction if the transaction is outgoing.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.FRAGMENT`.
|
||||
withdrawal_state (:class:`telegram.RevenueWithdrawalState`): Optional. State of the
|
||||
transaction if the transaction is outgoing.
|
||||
"""
|
||||
|
||||
__slots__ = ("withdrawal_state",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
withdrawal_state: Optional["RevenueWithdrawalState"] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.FRAGMENT, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.withdrawal_state: Optional[RevenueWithdrawalState] = withdrawal_state
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerFragment"]:
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["withdrawal_state"] = RevenueWithdrawalState.de_json(
|
||||
data.get("withdrawal_state"), bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class TransactionPartnerUser(TransactionPartner):
|
||||
"""Describes a transaction with a user.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`user` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
user (:class:`telegram.User`): Information about the user.
|
||||
affiliate (:class:`telegram.AffiliateInfo`, optional): Information about the affiliate that
|
||||
received a commission via this transaction
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
invoice_payload (:obj:`str`, optional): Bot-specified invoice payload.
|
||||
subscription_period (:class:`datetime.timedelta`, optional): The duration of the paid
|
||||
subscription
|
||||
|
||||
.. versionadded:: 21.8
|
||||
paid_media (Sequence[:class:`telegram.PaidMedia`], optional): Information about the paid
|
||||
media bought by the user.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
paid_media_payload (:obj:`str`, optional): Bot-specified paid media payload.
|
||||
|
||||
.. versionadded:: 21.6
|
||||
gift (:class:`telegram.Gift`, optional): The gift sent to the user by the bot
|
||||
|
||||
.. versionadded:: 21.8
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.USER`.
|
||||
user (:class:`telegram.User`): Information about the user.
|
||||
affiliate (:class:`telegram.AffiliateInfo`): Optional. Information about the affiliate that
|
||||
received a commission via this transaction
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
invoice_payload (:obj:`str`): Optional. Bot-specified invoice payload.
|
||||
subscription_period (:class:`datetime.timedelta`): Optional. The duration of the paid
|
||||
subscription
|
||||
|
||||
.. versionadded:: 21.8
|
||||
paid_media (tuple[:class:`telegram.PaidMedia`]): Optional. Information about the paid
|
||||
media bought by the user.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
paid_media_payload (:obj:`str`): Optional. Bot-specified paid media payload.
|
||||
|
||||
.. versionadded:: 21.6
|
||||
gift (:class:`telegram.Gift`): Optional. The gift sent to the user by the bot
|
||||
|
||||
.. versionadded:: 21.8
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"affiliate",
|
||||
"gift",
|
||||
"invoice_payload",
|
||||
"paid_media",
|
||||
"paid_media_payload",
|
||||
"subscription_period",
|
||||
"user",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user: "User",
|
||||
invoice_payload: Optional[str] = None,
|
||||
paid_media: Optional[Sequence[PaidMedia]] = None,
|
||||
paid_media_payload: Optional[str] = None,
|
||||
subscription_period: Optional[dtm.timedelta] = None,
|
||||
gift: Optional[Gift] = None,
|
||||
affiliate: Optional[AffiliateInfo] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.USER, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.user: User = user
|
||||
self.affiliate: Optional[AffiliateInfo] = affiliate
|
||||
self.invoice_payload: Optional[str] = invoice_payload
|
||||
self.paid_media: Optional[tuple[PaidMedia, ...]] = parse_sequence_arg(paid_media)
|
||||
self.paid_media_payload: Optional[str] = paid_media_payload
|
||||
self.subscription_period: Optional[dtm.timedelta] = subscription_period
|
||||
self.gift: Optional[Gift] = gift
|
||||
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.user,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerUser"]:
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["affiliate"] = AffiliateInfo.de_json(data.get("affiliate"), bot)
|
||||
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
|
||||
data["subscription_period"] = (
|
||||
dtm.timedelta(seconds=sp)
|
||||
if (sp := data.get("subscription_period")) is not None
|
||||
else None
|
||||
)
|
||||
data["gift"] = Gift.de_json(data.get("gift"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class TransactionPartnerOther(TransactionPartner):
|
||||
"""Describes a transaction with an unknown partner.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.OTHER`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=TransactionPartner.OTHER, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class TransactionPartnerTelegramAds(TransactionPartner):
|
||||
"""Describes a withdrawal transaction to the Telegram Ads platform.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.TELEGRAM_ADS`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=TransactionPartner.TELEGRAM_ADS, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class TransactionPartnerTelegramApi(TransactionPartner):
|
||||
"""Describes a transaction with payment for
|
||||
`paid broadcasting <https://core.telegram.org/bots/api#paid-broadcasts>`_.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`request_count` is equal.
|
||||
|
||||
.. versionadded:: 21.7
|
||||
|
||||
Args:
|
||||
request_count (:obj:`int`): The number of successful requests that exceeded regular limits
|
||||
and were therefore billed.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.TELEGRAM_API`.
|
||||
request_count (:obj:`int`): The number of successful requests that exceeded regular limits
|
||||
and were therefore billed.
|
||||
"""
|
||||
|
||||
__slots__ = ("request_count",)
|
||||
|
||||
def __init__(self, request_count: int, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=TransactionPartner.TELEGRAM_API, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.request_count: int = request_count
|
||||
self._id_attrs = (self.request_count,)
|
||||
|
||||
|
||||
class StarTransaction(TelegramObject):
|
||||
"""Describes a Telegram Star transaction.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`id`, :attr:`source`, and :attr:`receiver` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier of the transaction. Coincides with the identifer
|
||||
of the original transaction for refund transactions.
|
||||
Coincides with :attr:`SuccessfulPayment.telegram_payment_charge_id` for
|
||||
successful incoming payments from users.
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars transferred by the transaction.
|
||||
nanostar_amount (:obj:`int`, optional): The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars transferred by the transaction; from 0 to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
date (:obj:`datetime.datetime`): Date the transaction was created as a datetime object.
|
||||
source (:class:`telegram.TransactionPartner`, optional): Source of an incoming transaction
|
||||
(e.g., a user purchasing goods or services, Fragment refunding a failed withdrawal).
|
||||
Only for incoming transactions.
|
||||
receiver (:class:`telegram.TransactionPartner`, optional): Receiver of an outgoing
|
||||
transaction (e.g., a user for a purchase refund, Fragment for a withdrawal). Only for
|
||||
outgoing transactions.
|
||||
|
||||
Attributes:
|
||||
id (:obj:`str`): Unique identifier of the transaction. Coincides with the identifer
|
||||
of the original transaction for refund transactions.
|
||||
Coincides with :attr:`SuccessfulPayment.telegram_payment_charge_id` for
|
||||
successful incoming payments from users.
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars transferred by the transaction.
|
||||
nanostar_amount (:obj:`int`): Optional. The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars transferred by the transaction; from 0 to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
date (:obj:`datetime.datetime`): Date the transaction was created as a datetime object.
|
||||
source (:class:`telegram.TransactionPartner`): Optional. Source of an incoming transaction
|
||||
(e.g., a user purchasing goods or services, Fragment refunding a failed withdrawal).
|
||||
Only for incoming transactions.
|
||||
receiver (:class:`telegram.TransactionPartner`): Optional. Receiver of an outgoing
|
||||
transaction (e.g., a user for a purchase refund, Fragment for a withdrawal). Only for
|
||||
outgoing transactions.
|
||||
"""
|
||||
|
||||
__slots__ = ("amount", "date", "id", "nanostar_amount", "receiver", "source")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: str,
|
||||
amount: int,
|
||||
date: dtm.datetime,
|
||||
source: Optional[TransactionPartner] = None,
|
||||
receiver: Optional[TransactionPartner] = None,
|
||||
nanostar_amount: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.id: str = id
|
||||
self.amount: int = amount
|
||||
self.date: dtm.datetime = date
|
||||
self.source: Optional[TransactionPartner] = source
|
||||
self.receiver: Optional[TransactionPartner] = receiver
|
||||
self.nanostar_amount: Optional[int] = nanostar_amount
|
||||
|
||||
self._id_attrs = (
|
||||
self.id,
|
||||
self.source,
|
||||
self.receiver,
|
||||
)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["StarTransaction"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["date"] = from_timestamp(data.get("date", None), tzinfo=loc_tzinfo)
|
||||
|
||||
data["source"] = TransactionPartner.de_json(data.get("source"), bot)
|
||||
data["receiver"] = TransactionPartner.de_json(data.get("receiver"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class StarTransactions(TelegramObject):
|
||||
"""
|
||||
Contains a list of Telegram Star transactions.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`transactions` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
transactions (Sequence[:class:`telegram.StarTransaction`]): The list of transactions.
|
||||
|
||||
Attributes:
|
||||
transactions (tuple[:class:`telegram.StarTransaction`]): The list of transactions.
|
||||
"""
|
||||
|
||||
__slots__ = ("transactions",)
|
||||
|
||||
def __init__(
|
||||
self, transactions: Sequence[StarTransaction], *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.transactions: tuple[StarTransaction, ...] = parse_sequence_arg(transactions)
|
||||
|
||||
self._id_attrs = (self.transactions,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["StarTransactions"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["transactions"] = StarTransaction.de_list(data.get("transactions"), bot)
|
||||
return super().de_json(data=data, bot=bot)
|
0
telegram/_payment/stars/__init__.py
Normal file
0
telegram/_payment/stars/__init__.py
Normal file
121
telegram/_payment/stars/affiliateinfo.py
Normal file
121
telegram/_payment/stars/affiliateinfo.py
Normal file
|
@ -0,0 +1,121 @@
|
|||
#!/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/].
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes for Telegram Stars affiliates."""
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._chat import Chat
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class AffiliateInfo(TelegramObject):
|
||||
"""Contains information about the affiliate that received a commission via this transaction.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`affiliate_user`, :attr:`affiliate_chat`,
|
||||
:attr:`commission_per_mille`, :attr:`amount`, and :attr:`nanostar_amount` are equal.
|
||||
|
||||
.. versionadded:: 21.9
|
||||
|
||||
Args:
|
||||
affiliate_user (:class:`telegram.User`, optional): The bot or the user that received an
|
||||
affiliate commission if it was received by a bot or a user
|
||||
affiliate_chat (:class:`telegram.Chat`, optional): The chat that received an affiliate
|
||||
commission if it was received by a chat
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the affiliate
|
||||
for each 1000 Telegram Stars received by the bot from referred users
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars received by the affiliate from the
|
||||
transaction, rounded to 0; can be negative for refunds
|
||||
nanostar_amount (:obj:`int`, optional): The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars received by the affiliate; from
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MIN_AMOUNT` to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`;
|
||||
can be negative for refunds
|
||||
|
||||
Attributes:
|
||||
affiliate_user (:class:`telegram.User`): Optional. The bot or the user that received an
|
||||
affiliate commission if it was received by a bot or a user
|
||||
affiliate_chat (:class:`telegram.Chat`): Optional. The chat that received an affiliate
|
||||
commission if it was received by a chat
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the affiliate
|
||||
for each 1000 Telegram Stars received by the bot from referred users
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars received by the affiliate from the
|
||||
transaction, rounded to 0; can be negative for refunds
|
||||
nanostar_amount (:obj:`int`): Optional. The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars received by the affiliate; from
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MIN_AMOUNT` to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`;
|
||||
can be negative for refunds
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"affiliate_chat",
|
||||
"affiliate_user",
|
||||
"amount",
|
||||
"commission_per_mille",
|
||||
"nanostar_amount",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
commission_per_mille: int,
|
||||
amount: int,
|
||||
affiliate_user: Optional["User"] = None,
|
||||
affiliate_chat: Optional["Chat"] = None,
|
||||
nanostar_amount: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.affiliate_user: Optional[User] = affiliate_user
|
||||
self.affiliate_chat: Optional[Chat] = affiliate_chat
|
||||
self.commission_per_mille: int = commission_per_mille
|
||||
self.amount: int = amount
|
||||
self.nanostar_amount: Optional[int] = nanostar_amount
|
||||
|
||||
self._id_attrs = (
|
||||
self.affiliate_user,
|
||||
self.affiliate_chat,
|
||||
self.commission_per_mille,
|
||||
self.amount,
|
||||
self.nanostar_amount,
|
||||
)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["AffiliateInfo"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["affiliate_user"] = User.de_json(data.get("affiliate_user"), bot)
|
||||
data["affiliate_chat"] = Chat.de_json(data.get("affiliate_chat"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
188
telegram/_payment/stars/revenuewithdrawalstate.py
Normal file
188
telegram/_payment/stars/revenuewithdrawalstate.py
Normal file
|
@ -0,0 +1,188 @@
|
|||
#!/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/].
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes for Telegram Stars Revenue Withdrawals."""
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class RevenueWithdrawalState(TelegramObject):
|
||||
"""This object describes the state of a revenue withdrawal operation. Currently, it can be one
|
||||
of:
|
||||
|
||||
* :class:`telegram.RevenueWithdrawalStatePending`
|
||||
* :class:`telegram.RevenueWithdrawalStateSucceeded`
|
||||
* :class:`telegram.RevenueWithdrawalStateFailed`
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The type of the state.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state.
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
PENDING: Final[str] = constants.RevenueWithdrawalStateType.PENDING
|
||||
""":const:`telegram.constants.RevenueWithdrawalStateType.PENDING`"""
|
||||
SUCCEEDED: Final[str] = constants.RevenueWithdrawalStateType.SUCCEEDED
|
||||
""":const:`telegram.constants.RevenueWithdrawalStateType.SUCCEEDED`"""
|
||||
FAILED: Final[str] = constants.RevenueWithdrawalStateType.FAILED
|
||||
""":const:`telegram.constants.RevenueWithdrawalStateType.FAILED`"""
|
||||
|
||||
def __init__(self, type: str, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: str = enum.get_member(constants.RevenueWithdrawalStateType, type, type)
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["RevenueWithdrawalState"]:
|
||||
"""Converts JSON data to the appropriate :class:`RevenueWithdrawalState` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
Args:
|
||||
data (dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if (cls is RevenueWithdrawalState and not data) or data is None:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[RevenueWithdrawalState]] = {
|
||||
cls.PENDING: RevenueWithdrawalStatePending,
|
||||
cls.SUCCEEDED: RevenueWithdrawalStateSucceeded,
|
||||
cls.FAILED: RevenueWithdrawalStateFailed,
|
||||
}
|
||||
|
||||
if cls is RevenueWithdrawalState and data.get("type") in _class_mapping:
|
||||
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class RevenueWithdrawalStatePending(RevenueWithdrawalState):
|
||||
"""The withdrawal is in progress.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state, always
|
||||
:tg-const:`telegram.RevenueWithdrawalState.PENDING`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=RevenueWithdrawalState.PENDING, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState):
|
||||
"""The withdrawal succeeded.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`date` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
date (:obj:`datetime.datetime`): Date the withdrawal was completed as a datetime object.
|
||||
url (:obj:`str`): An HTTPS URL that can be used to see transaction details.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state, always
|
||||
:tg-const:`telegram.RevenueWithdrawalState.SUCCEEDED`.
|
||||
date (:obj:`datetime.datetime`): Date the withdrawal was completed as a datetime object.
|
||||
url (:obj:`str`): An HTTPS URL that can be used to see transaction details.
|
||||
"""
|
||||
|
||||
__slots__ = ("date", "url")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
date: dtm.datetime,
|
||||
url: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=RevenueWithdrawalState.SUCCEEDED, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.date: dtm.datetime = date
|
||||
self.url: str = url
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.date,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["RevenueWithdrawalStateSucceeded"]:
|
||||
"""See :meth:`telegram.RevenueWithdrawalState.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["date"] = from_timestamp(data.get("date", None), tzinfo=loc_tzinfo)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class RevenueWithdrawalStateFailed(RevenueWithdrawalState):
|
||||
"""The withdrawal failed and the transaction was refunded.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the state, always
|
||||
:tg-const:`telegram.RevenueWithdrawalState.FAILED`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=RevenueWithdrawalState.FAILED, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
172
telegram/_payment/stars/startransactions.py
Normal file
172
telegram/_payment/stars/startransactions.py
Normal file
|
@ -0,0 +1,172 @@
|
|||
#!/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/].
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes for Telegram Stars transactions."""
|
||||
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
from .transactionpartner import TransactionPartner
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class StarTransaction(TelegramObject):
|
||||
"""Describes a Telegram Star transaction.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`id`, :attr:`source`, and :attr:`receiver` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier of the transaction. Coincides with the identifer
|
||||
of the original transaction for refund transactions.
|
||||
Coincides with :attr:`SuccessfulPayment.telegram_payment_charge_id` for
|
||||
successful incoming payments from users.
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars transferred by the transaction.
|
||||
nanostar_amount (:obj:`int`, optional): The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars transferred by the transaction; from 0 to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`
|
||||
|
||||
.. versionadded:: 21.9
|
||||
date (:obj:`datetime.datetime`): Date the transaction was created as a datetime object.
|
||||
source (:class:`telegram.TransactionPartner`, optional): Source of an incoming transaction
|
||||
(e.g., a user purchasing goods or services, Fragment refunding a failed withdrawal).
|
||||
Only for incoming transactions.
|
||||
receiver (:class:`telegram.TransactionPartner`, optional): Receiver of an outgoing
|
||||
transaction (e.g., a user for a purchase refund, Fragment for a withdrawal). Only for
|
||||
outgoing transactions.
|
||||
|
||||
Attributes:
|
||||
id (:obj:`str`): Unique identifier of the transaction. Coincides with the identifer
|
||||
of the original transaction for refund transactions.
|
||||
Coincides with :attr:`SuccessfulPayment.telegram_payment_charge_id` for
|
||||
successful incoming payments from users.
|
||||
amount (:obj:`int`): Integer amount of Telegram Stars transferred by the transaction.
|
||||
nanostar_amount (:obj:`int`): Optional. The number of
|
||||
:tg-const:`~telegram.constants.StarTransactions.NANOSTAR_VALUE` shares of Telegram
|
||||
Stars transferred by the transaction; from 0 to
|
||||
:tg-const:`~telegram.constants.StarTransactionsLimit.NANOSTAR_MAX_AMOUNT`
|
||||
|
||||
.. versionadded:: 21.9
|
||||
date (:obj:`datetime.datetime`): Date the transaction was created as a datetime object.
|
||||
source (:class:`telegram.TransactionPartner`): Optional. Source of an incoming transaction
|
||||
(e.g., a user purchasing goods or services, Fragment refunding a failed withdrawal).
|
||||
Only for incoming transactions.
|
||||
receiver (:class:`telegram.TransactionPartner`): Optional. Receiver of an outgoing
|
||||
transaction (e.g., a user for a purchase refund, Fragment for a withdrawal). Only for
|
||||
outgoing transactions.
|
||||
"""
|
||||
|
||||
__slots__ = ("amount", "date", "id", "nanostar_amount", "receiver", "source")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: str,
|
||||
amount: int,
|
||||
date: dtm.datetime,
|
||||
source: Optional[TransactionPartner] = None,
|
||||
receiver: Optional[TransactionPartner] = None,
|
||||
nanostar_amount: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.id: str = id
|
||||
self.amount: int = amount
|
||||
self.date: dtm.datetime = date
|
||||
self.source: Optional[TransactionPartner] = source
|
||||
self.receiver: Optional[TransactionPartner] = receiver
|
||||
self.nanostar_amount: Optional[int] = nanostar_amount
|
||||
|
||||
self._id_attrs = (
|
||||
self.id,
|
||||
self.source,
|
||||
self.receiver,
|
||||
)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["StarTransaction"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["date"] = from_timestamp(data.get("date", None), tzinfo=loc_tzinfo)
|
||||
|
||||
data["source"] = TransactionPartner.de_json(data.get("source"), bot)
|
||||
data["receiver"] = TransactionPartner.de_json(data.get("receiver"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class StarTransactions(TelegramObject):
|
||||
"""
|
||||
Contains a list of Telegram Star transactions.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`transactions` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
transactions (Sequence[:class:`telegram.StarTransaction`]): The list of transactions.
|
||||
|
||||
Attributes:
|
||||
transactions (tuple[:class:`telegram.StarTransaction`]): The list of transactions.
|
||||
"""
|
||||
|
||||
__slots__ = ("transactions",)
|
||||
|
||||
def __init__(
|
||||
self, transactions: Sequence[StarTransaction], *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.transactions: tuple[StarTransaction, ...] = parse_sequence_arg(transactions)
|
||||
|
||||
self._id_attrs = (self.transactions,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["StarTransactions"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["transactions"] = StarTransaction.de_list(data.get("transactions"), bot)
|
||||
return super().de_json(data=data, bot=bot)
|
405
telegram/_payment/stars/transactionpartner.py
Normal file
405
telegram/_payment/stars/transactionpartner.py
Normal file
|
@ -0,0 +1,405 @@
|
|||
#!/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/].
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes for Telegram Stars transaction partners."""
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
from telegram._gifts import Gift
|
||||
from telegram._paidmedia import PaidMedia
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
from .affiliateinfo import AffiliateInfo
|
||||
from .revenuewithdrawalstate import RevenueWithdrawalState
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class TransactionPartner(TelegramObject):
|
||||
"""This object describes the source of a transaction, or its recipient for outgoing
|
||||
transactions. Currently, it can be one of:
|
||||
|
||||
* :class:`TransactionPartnerUser`
|
||||
* :class:`TransactionPartnerAffiliateProgram`
|
||||
* :class:`TransactionPartnerFragment`
|
||||
* :class:`TransactionPartnerTelegramAds`
|
||||
* :class:`TransactionPartnerTelegramApi`
|
||||
* :class:`TransactionPartnerOther`
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The type of the transaction partner.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner.
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
AFFILIATE_PROGRAM: Final[str] = constants.TransactionPartnerType.AFFILIATE_PROGRAM
|
||||
""":const:`telegram.constants.TransactionPartnerType.AFFILIATE_PROGRAM`
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
FRAGMENT: Final[str] = constants.TransactionPartnerType.FRAGMENT
|
||||
""":const:`telegram.constants.TransactionPartnerType.FRAGMENT`"""
|
||||
OTHER: Final[str] = constants.TransactionPartnerType.OTHER
|
||||
""":const:`telegram.constants.TransactionPartnerType.OTHER`"""
|
||||
TELEGRAM_ADS: Final[str] = constants.TransactionPartnerType.TELEGRAM_ADS
|
||||
""":const:`telegram.constants.TransactionPartnerType.TELEGRAM_ADS`"""
|
||||
TELEGRAM_API: Final[str] = constants.TransactionPartnerType.TELEGRAM_API
|
||||
""":const:`telegram.constants.TransactionPartnerType.TELEGRAM_API`"""
|
||||
USER: Final[str] = constants.TransactionPartnerType.USER
|
||||
""":const:`telegram.constants.TransactionPartnerType.USER`"""
|
||||
|
||||
def __init__(self, type: str, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: str = enum.get_member(constants.TransactionPartnerType, type, type)
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartner"]:
|
||||
"""Converts JSON data to the appropriate :class:`TransactionPartner` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
Args:
|
||||
data (dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if (cls is TransactionPartner and not data) or data is None:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[TransactionPartner]] = {
|
||||
cls.AFFILIATE_PROGRAM: TransactionPartnerAffiliateProgram,
|
||||
cls.FRAGMENT: TransactionPartnerFragment,
|
||||
cls.USER: TransactionPartnerUser,
|
||||
cls.TELEGRAM_ADS: TransactionPartnerTelegramAds,
|
||||
cls.TELEGRAM_API: TransactionPartnerTelegramApi,
|
||||
cls.OTHER: TransactionPartnerOther,
|
||||
}
|
||||
|
||||
if cls is TransactionPartner and data.get("type") in _class_mapping:
|
||||
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class TransactionPartnerAffiliateProgram(TransactionPartner):
|
||||
"""Describes the affiliate program that issued the affiliate commission received via this
|
||||
transaction.
|
||||
|
||||
This object is comparable in terms of equality. Two objects of this class are considered equal,
|
||||
if their :attr:`commission_per_mille` are equal.
|
||||
|
||||
.. versionadded:: 21.9
|
||||
|
||||
Args:
|
||||
sponsor_user (:class:`telegram.User`, optional): Information about the bot that sponsored
|
||||
the affiliate program
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the bot for
|
||||
each 1000 Telegram Stars received by the affiliate program sponsor from referred users.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.AFFILIATE_PROGRAM`.
|
||||
sponsor_user (:class:`telegram.User`): Optional. Information about the bot that sponsored
|
||||
the affiliate program
|
||||
commission_per_mille (:obj:`int`): The number of Telegram Stars received by the bot for
|
||||
each 1000 Telegram Stars received by the affiliate program sponsor from referred users.
|
||||
"""
|
||||
|
||||
__slots__ = ("commission_per_mille", "sponsor_user")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
commission_per_mille: int,
|
||||
sponsor_user: Optional["User"] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.AFFILIATE_PROGRAM, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.sponsor_user: Optional[User] = sponsor_user
|
||||
self.commission_per_mille: int = commission_per_mille
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.commission_per_mille,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerAffiliateProgram"]:
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["sponsor_user"] = User.de_json(data.get("sponsor_user"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class TransactionPartnerFragment(TransactionPartner):
|
||||
"""Describes a withdrawal transaction with Fragment.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
withdrawal_state (:class:`telegram.RevenueWithdrawalState`, optional): State of the
|
||||
transaction if the transaction is outgoing.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.FRAGMENT`.
|
||||
withdrawal_state (:class:`telegram.RevenueWithdrawalState`): Optional. State of the
|
||||
transaction if the transaction is outgoing.
|
||||
"""
|
||||
|
||||
__slots__ = ("withdrawal_state",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
withdrawal_state: Optional["RevenueWithdrawalState"] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.FRAGMENT, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.withdrawal_state: Optional[RevenueWithdrawalState] = withdrawal_state
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerFragment"]:
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["withdrawal_state"] = RevenueWithdrawalState.de_json(
|
||||
data.get("withdrawal_state"), bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class TransactionPartnerUser(TransactionPartner):
|
||||
"""Describes a transaction with a user.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`user` are equal.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Args:
|
||||
user (:class:`telegram.User`): Information about the user.
|
||||
affiliate (:class:`telegram.AffiliateInfo`, optional): Information about the affiliate that
|
||||
received a commission via this transaction
|
||||
|
||||
.. versionadded:: 21.9
|
||||
invoice_payload (:obj:`str`, optional): Bot-specified invoice payload.
|
||||
subscription_period (:class:`datetime.timedelta`, optional): The duration of the paid
|
||||
subscription
|
||||
|
||||
.. versionadded:: 21.8
|
||||
paid_media (Sequence[:class:`telegram.PaidMedia`], optional): Information about the paid
|
||||
media bought by the user.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
paid_media_payload (:obj:`str`, optional): Bot-specified paid media payload.
|
||||
|
||||
.. versionadded:: 21.6
|
||||
gift (:class:`telegram.Gift`, optional): The gift sent to the user by the bot
|
||||
|
||||
.. versionadded:: 21.8
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.USER`.
|
||||
user (:class:`telegram.User`): Information about the user.
|
||||
affiliate (:class:`telegram.AffiliateInfo`): Optional. Information about the affiliate that
|
||||
received a commission via this transaction
|
||||
|
||||
.. versionadded:: 21.9
|
||||
invoice_payload (:obj:`str`): Optional. Bot-specified invoice payload.
|
||||
subscription_period (:class:`datetime.timedelta`): Optional. The duration of the paid
|
||||
subscription
|
||||
|
||||
.. versionadded:: 21.8
|
||||
paid_media (tuple[:class:`telegram.PaidMedia`]): Optional. Information about the paid
|
||||
media bought by the user.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
paid_media_payload (:obj:`str`): Optional. Bot-specified paid media payload.
|
||||
|
||||
.. versionadded:: 21.6
|
||||
gift (:class:`telegram.Gift`): Optional. The gift sent to the user by the bot
|
||||
|
||||
.. versionadded:: 21.8
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"affiliate",
|
||||
"gift",
|
||||
"invoice_payload",
|
||||
"paid_media",
|
||||
"paid_media_payload",
|
||||
"subscription_period",
|
||||
"user",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user: "User",
|
||||
invoice_payload: Optional[str] = None,
|
||||
paid_media: Optional[Sequence[PaidMedia]] = None,
|
||||
paid_media_payload: Optional[str] = None,
|
||||
subscription_period: Optional[dtm.timedelta] = None,
|
||||
gift: Optional[Gift] = None,
|
||||
affiliate: Optional[AffiliateInfo] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.USER, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.user: User = user
|
||||
self.affiliate: Optional[AffiliateInfo] = affiliate
|
||||
self.invoice_payload: Optional[str] = invoice_payload
|
||||
self.paid_media: Optional[tuple[PaidMedia, ...]] = parse_sequence_arg(paid_media)
|
||||
self.paid_media_payload: Optional[str] = paid_media_payload
|
||||
self.subscription_period: Optional[dtm.timedelta] = subscription_period
|
||||
self.gift: Optional[Gift] = gift
|
||||
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.user,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerUser"]:
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["affiliate"] = AffiliateInfo.de_json(data.get("affiliate"), bot)
|
||||
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
|
||||
data["subscription_period"] = (
|
||||
dtm.timedelta(seconds=sp)
|
||||
if (sp := data.get("subscription_period")) is not None
|
||||
else None
|
||||
)
|
||||
data["gift"] = Gift.de_json(data.get("gift"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class TransactionPartnerOther(TransactionPartner):
|
||||
"""Describes a transaction with an unknown partner.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.OTHER`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=TransactionPartner.OTHER, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class TransactionPartnerTelegramAds(TransactionPartner):
|
||||
"""Describes a withdrawal transaction to the Telegram Ads platform.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.TELEGRAM_ADS`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=TransactionPartner.TELEGRAM_ADS, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class TransactionPartnerTelegramApi(TransactionPartner):
|
||||
"""Describes a transaction with payment for
|
||||
`paid broadcasting <https://core.telegram.org/bots/api#paid-broadcasts>`_.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`request_count` is equal.
|
||||
|
||||
.. versionadded:: 21.7
|
||||
|
||||
Args:
|
||||
request_count (:obj:`int`): The number of successful requests that exceeded regular limits
|
||||
and were therefore billed.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.TELEGRAM_API`.
|
||||
request_count (:obj:`int`): The number of successful requests that exceeded regular limits
|
||||
and were therefore billed.
|
||||
"""
|
||||
|
||||
__slots__ = ("request_count",)
|
||||
|
||||
def __init__(self, request_count: int, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(type=TransactionPartner.TELEGRAM_API, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.request_count: int = request_count
|
||||
self._id_attrs = (self.request_count,)
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Poll."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
|
@ -446,7 +446,7 @@ class Poll(TelegramObject):
|
|||
explanation: Optional[str] = None,
|
||||
explanation_entities: Optional[Sequence[MessageEntity]] = None,
|
||||
open_period: Optional[int] = None,
|
||||
close_date: Optional[datetime.datetime] = None,
|
||||
close_date: Optional[dtm.datetime] = None,
|
||||
question_entities: Optional[Sequence[MessageEntity]] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
|
@ -466,7 +466,7 @@ class Poll(TelegramObject):
|
|||
explanation_entities
|
||||
)
|
||||
self.open_period: Optional[int] = open_period
|
||||
self.close_date: Optional[datetime.datetime] = close_date
|
||||
self.close_date: Optional[dtm.datetime] = close_date
|
||||
self.question_entities: tuple[MessageEntity, ...] = parse_sequence_arg(question_entities)
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""Base class for Telegram Objects."""
|
||||
import contextlib
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
import json
|
||||
from collections.abc import Iterator, Mapping, Sized
|
||||
|
@ -634,9 +634,9 @@ class TelegramObject:
|
|||
val.append(item)
|
||||
out[key] = val
|
||||
|
||||
elif isinstance(value, datetime.datetime):
|
||||
elif isinstance(value, dtm.datetime):
|
||||
out[key] = to_timestamp(value)
|
||||
elif isinstance(value, datetime.timedelta):
|
||||
elif isinstance(value, dtm.timedelta):
|
||||
out[key] = value.total_seconds()
|
||||
|
||||
for key in pop_keys:
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram User."""
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
|
@ -1582,7 +1582,7 @@ class User(TelegramObject):
|
|||
explanation: Optional[str] = None,
|
||||
explanation_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
open_period: Optional[int] = None,
|
||||
close_date: Optional[Union[int, datetime]] = None,
|
||||
close_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
explanation_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
|
|
@ -51,6 +51,6 @@ class Version(NamedTuple):
|
|||
|
||||
|
||||
__version_info__: Final[Version] = Version(
|
||||
major=21, minor=8, micro=0, releaselevel="final", serial=0
|
||||
major=21, minor=9, micro=0, releaselevel="final", serial=0
|
||||
)
|
||||
__version__: Final[str] = str(__version_info__)
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram WebhookInfo."""
|
||||
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
|
@ -126,12 +126,12 @@ class WebhookInfo(TelegramObject):
|
|||
url: str,
|
||||
has_custom_certificate: bool,
|
||||
pending_update_count: int,
|
||||
last_error_date: Optional[datetime] = None,
|
||||
last_error_date: Optional[dtm.datetime] = None,
|
||||
last_error_message: Optional[str] = None,
|
||||
max_connections: Optional[int] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
ip_address: Optional[str] = None,
|
||||
last_synchronization_error_date: Optional[datetime] = None,
|
||||
last_synchronization_error_date: Optional[dtm.datetime] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -143,11 +143,13 @@ class WebhookInfo(TelegramObject):
|
|||
|
||||
# Optional
|
||||
self.ip_address: Optional[str] = ip_address
|
||||
self.last_error_date: Optional[datetime] = last_error_date
|
||||
self.last_error_date: Optional[dtm.datetime] = last_error_date
|
||||
self.last_error_message: Optional[str] = last_error_message
|
||||
self.max_connections: Optional[int] = max_connections
|
||||
self.allowed_updates: tuple[str, ...] = parse_sequence_arg(allowed_updates)
|
||||
self.last_synchronization_error_date: Optional[datetime] = last_synchronization_error_date
|
||||
self.last_synchronization_error_date: Optional[dtm.datetime] = (
|
||||
last_synchronization_error_date
|
||||
)
|
||||
|
||||
self._id_attrs = (
|
||||
self.url,
|
||||
|
|
|
@ -107,7 +107,7 @@ __all__ = [
|
|||
"WebhookLimit",
|
||||
]
|
||||
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
import sys
|
||||
from enum import Enum
|
||||
from typing import Final, NamedTuple, Optional
|
||||
|
@ -172,7 +172,7 @@ SUPPORTED_WEBHOOK_PORTS: Final[list[int]] = [443, 80, 88, 8443]
|
|||
#: This date literal is used in :class:`telegram.InaccessibleMessage`
|
||||
#:
|
||||
#: .. versionadded:: 20.8
|
||||
ZERO_DATE: Final[datetime.datetime] = datetime.datetime(1970, 1, 1, tzinfo=UTC)
|
||||
ZERO_DATE: Final[dtm.datetime] = dtm.datetime(1970, 1, 1, tzinfo=UTC)
|
||||
|
||||
|
||||
class AccentColor(Enum):
|
||||
|
@ -2466,7 +2466,7 @@ class StarTransactions(FloatEnum):
|
|||
The enum members of this enumeration are instances of :class:`float` and can be treated as
|
||||
such.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
@ -2499,7 +2499,7 @@ class StarTransactionsLimit(IntEnum):
|
|||
""":obj:`int`: Minimum value allowed for :paramref:`~telegram.AffiliateInfo.nanostar_amount`
|
||||
parameter of :class:`telegram.AffiliateInfo`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
NANOSTAR_MAX_AMOUNT = 999999999
|
||||
""":obj:`int`: Maximum value allowed for :paramref:`~telegram.StarTransaction.nanostar_amount`
|
||||
|
@ -2507,7 +2507,7 @@ class StarTransactionsLimit(IntEnum):
|
|||
:paramref:`~telegram.AffiliateInfo.nanostar_amount` parameter of
|
||||
:class:`telegram.AffiliateInfo`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
|
||||
|
||||
|
@ -2656,7 +2656,7 @@ class TransactionPartnerType(StringEnum):
|
|||
AFFILIATE_PROGRAM = "affiliate_program"
|
||||
""":obj:`str`: Transaction with Affiliate Program.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
FRAGMENT = "fragment"
|
||||
""":obj:`str`: Withdrawal transaction with Fragment."""
|
||||
|
@ -2954,7 +2954,7 @@ class InvoiceLimit(IntEnum):
|
|||
|
||||
.. versionadded:: 21.6
|
||||
"""
|
||||
SUBSCRIPTION_PERIOD = datetime.timedelta(days=30).total_seconds()
|
||||
SUBSCRIPTION_PERIOD = dtm.timedelta(days=30).total_seconds()
|
||||
""":obj:`int`: The period of time for which the subscription is active before
|
||||
the next payment, passed as :paramref:`~telegram.Bot.create_invoice_link.subscription_period`
|
||||
parameter of :meth:`telegram.Bot.create_invoice_link`.
|
||||
|
@ -2965,7 +2965,7 @@ class InvoiceLimit(IntEnum):
|
|||
""":obj:`int`: The maximum price of a subscription created wtih
|
||||
:meth:`telegram.Bot.create_invoice_link`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
@ -826,7 +826,7 @@ class Application(
|
|||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.get_updates`.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
.. versionchanged:: 21.9
|
||||
Accepts any :class:`collections.abc.Sequence` as input instead of just a list
|
||||
close_loop (:obj:`bool`, optional): If :obj:`True`, the current event loop will be
|
||||
closed upon shutdown. Defaults to :obj:`True`.
|
||||
|
@ -960,7 +960,7 @@ class Application(
|
|||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.set_webhook`.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
.. versionchanged:: 21.9
|
||||
Accepts any :class:`collections.abc.Sequence` as input instead of just a list
|
||||
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
|
||||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the CallbackDataCache class."""
|
||||
import datetime as dtm
|
||||
import time
|
||||
from collections.abc import MutableMapping
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
||||
from uuid import uuid4
|
||||
|
||||
|
@ -431,7 +431,9 @@ class CallbackDataCache:
|
|||
with contextlib.suppress(KeyError):
|
||||
self._keyboard_data.pop(keyboard_uuid)
|
||||
|
||||
def clear_callback_data(self, time_cutoff: Optional[Union[float, datetime]] = None) -> None:
|
||||
def clear_callback_data(
|
||||
self, time_cutoff: Optional[Union[float, dtm.datetime]] = None
|
||||
) -> None:
|
||||
"""Clears the stored callback data.
|
||||
|
||||
Args:
|
||||
|
@ -447,13 +449,13 @@ class CallbackDataCache:
|
|||
self.__clear(self._callback_queries)
|
||||
|
||||
def __clear(
|
||||
self, mapping: MutableMapping, time_cutoff: Optional[Union[float, datetime]] = None
|
||||
self, mapping: MutableMapping, time_cutoff: Optional[Union[float, dtm.datetime]] = None
|
||||
) -> None:
|
||||
if not time_cutoff:
|
||||
mapping.clear()
|
||||
return
|
||||
|
||||
if isinstance(time_cutoff, datetime):
|
||||
if isinstance(time_cutoff, dtm.datetime):
|
||||
effective_cutoff = to_float_timestamp(
|
||||
time_cutoff, tzinfo=self.bot.defaults.tzinfo if self.bot.defaults else None
|
||||
)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the class Defaults, which allows passing default values to Application."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from typing import Any, NoReturn, Optional, final
|
||||
|
||||
from telegram import LinkPreviewOptions
|
||||
|
@ -132,7 +132,7 @@ class Defaults:
|
|||
disable_notification: Optional[bool] = None,
|
||||
disable_web_page_preview: Optional[bool] = None,
|
||||
quote: Optional[bool] = None,
|
||||
tzinfo: datetime.tzinfo = UTC,
|
||||
tzinfo: dtm.tzinfo = UTC,
|
||||
block: bool = True,
|
||||
allow_sending_without_reply: Optional[bool] = None,
|
||||
protect_content: Optional[bool] = None,
|
||||
|
@ -142,7 +142,7 @@ class Defaults:
|
|||
self._parse_mode: Optional[str] = parse_mode
|
||||
self._disable_notification: Optional[bool] = disable_notification
|
||||
self._allow_sending_without_reply: Optional[bool] = allow_sending_without_reply
|
||||
self._tzinfo: datetime.tzinfo = tzinfo
|
||||
self._tzinfo: dtm.tzinfo = tzinfo
|
||||
self._block: bool = block
|
||||
self._protect_content: Optional[bool] = protect_content
|
||||
|
||||
|
@ -356,7 +356,7 @@ class Defaults:
|
|||
raise AttributeError("You can not assign a new value to quote after initialization.")
|
||||
|
||||
@property
|
||||
def tzinfo(self) -> datetime.tzinfo:
|
||||
def tzinfo(self) -> dtm.tzinfo:
|
||||
""":obj:`tzinfo`: A timezone to be used for all date(time) objects appearing
|
||||
throughout PTB.
|
||||
"""
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Bot with convenience extensions."""
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
from copy import copy
|
||||
from datetime import datetime, timedelta
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
|
@ -448,7 +448,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
data[key] = self.defaults.api_defaults.get(key, val.value)
|
||||
|
||||
# 2)
|
||||
elif isinstance(val, datetime):
|
||||
elif isinstance(val, dtm.datetime):
|
||||
data[key] = to_timestamp(val, tzinfo=self.defaults.tzinfo)
|
||||
|
||||
# 3)
|
||||
|
@ -1110,7 +1110,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: int,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
until_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
revoke_messages: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -1157,7 +1157,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
async def create_chat_invite_link(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
expire_date: Optional[Union[int, datetime]] = None,
|
||||
expire_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
member_limit: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
creates_join_request: Optional[bool] = None,
|
||||
|
@ -1204,7 +1204,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
send_phone_number_to_provider: Optional[bool] = None,
|
||||
send_email_to_provider: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
subscription_period: Optional[Union[int, timedelta]] = None,
|
||||
subscription_period: Optional[Union[int, dtm.timedelta]] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -1426,7 +1426,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
|
||||
async def delete_sticker_from_set(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -1468,7 +1468,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
self,
|
||||
chat_id: Union[str, int],
|
||||
invite_link: Union[str, "ChatInviteLink"],
|
||||
expire_date: Optional[Union[int, datetime]] = None,
|
||||
expire_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
member_limit: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
creates_join_request: Optional[bool] = None,
|
||||
|
@ -2375,7 +2375,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
chat_id: Union[str, int],
|
||||
user_id: int,
|
||||
permissions: ChatPermissions,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
until_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
use_independent_chat_permissions: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -3055,7 +3055,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
explanation: Optional[str] = None,
|
||||
explanation_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
open_period: Optional[int] = None,
|
||||
close_date: Optional[Union[int, datetime]] = None,
|
||||
close_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
explanation_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
|
@ -3426,7 +3426,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
self,
|
||||
user_id: int,
|
||||
emoji_status_custom_emoji_id: Optional[str] = None,
|
||||
emoji_status_expiration_date: Optional[Union[int, datetime]] = None,
|
||||
emoji_status_expiration_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -3660,7 +3660,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
|
||||
async def set_sticker_position_in_set(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
position: int,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -4114,7 +4114,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
|
||||
async def set_sticker_emoji_list(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
emoji_list: Sequence[str],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -4136,7 +4136,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
|
||||
async def set_sticker_keywords(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
keywords: Optional[Sequence[str]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -4158,7 +4158,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
|
||||
async def set_sticker_mask_position(
|
||||
self,
|
||||
sticker: str,
|
||||
sticker: Union[str, "Sticker"],
|
||||
mask_position: Optional[MaskPosition] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -4250,7 +4250,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
self,
|
||||
user_id: int,
|
||||
name: str,
|
||||
old_sticker: str,
|
||||
old_sticker: Union[str, "Sticker"],
|
||||
sticker: "InputSticker",
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the ConversationHandler."""
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any, Final, Generic, NoReturn, Optional, Union, cast
|
||||
|
||||
|
@ -291,7 +291,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]):
|
|||
per_chat: bool = True,
|
||||
per_user: bool = True,
|
||||
per_message: bool = False,
|
||||
conversation_timeout: Optional[Union[float, datetime.timedelta]] = None,
|
||||
conversation_timeout: Optional[Union[float, dtm.timedelta]] = None,
|
||||
name: Optional[str] = None,
|
||||
persistent: bool = False,
|
||||
map_to_parent: Optional[dict[object, object]] = None,
|
||||
|
@ -319,9 +319,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]):
|
|||
self._per_user: bool = per_user
|
||||
self._per_chat: bool = per_chat
|
||||
self._per_message: bool = per_message
|
||||
self._conversation_timeout: Optional[Union[float, datetime.timedelta]] = (
|
||||
conversation_timeout
|
||||
)
|
||||
self._conversation_timeout: Optional[Union[float, dtm.timedelta]] = conversation_timeout
|
||||
self._name: Optional[str] = name
|
||||
self._map_to_parent: Optional[dict[object, object]] = map_to_parent
|
||||
|
||||
|
@ -530,7 +528,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]):
|
|||
@property
|
||||
def conversation_timeout(
|
||||
self,
|
||||
) -> Optional[Union[float, datetime.timedelta]]:
|
||||
) -> Optional[Union[float, dtm.timedelta]]:
|
||||
""":obj:`float` | :obj:`datetime.timedelta`: Optional. When this
|
||||
handler is inactive more than this timeout (in seconds), it will be automatically
|
||||
ended.
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the classes JobQueue and Job."""
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
import weakref
|
||||
from typing import TYPE_CHECKING, Any, Generic, Optional, Union, cast, overload
|
||||
|
||||
|
@ -155,7 +155,7 @@ class JobQueue(Generic[CCT]):
|
|||
dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary.
|
||||
|
||||
"""
|
||||
timezone: datetime.tzinfo = UTC
|
||||
timezone: dtm.tzinfo = UTC
|
||||
if (
|
||||
self._application
|
||||
and isinstance(self.application.bot, ExtBot)
|
||||
|
@ -168,8 +168,8 @@ class JobQueue(Generic[CCT]):
|
|||
"executors": {"default": self._executor},
|
||||
}
|
||||
|
||||
def _tz_now(self) -> datetime.datetime:
|
||||
return datetime.datetime.now(self.scheduler.timezone)
|
||||
def _tz_now(self) -> dtm.datetime:
|
||||
return dtm.datetime.now(self.scheduler.timezone)
|
||||
|
||||
@overload
|
||||
def _parse_time_input(self, time: None, shift_day: bool = False) -> None: ...
|
||||
|
@ -177,29 +177,29 @@ class JobQueue(Generic[CCT]):
|
|||
@overload
|
||||
def _parse_time_input(
|
||||
self,
|
||||
time: Union[float, datetime.timedelta, datetime.datetime, datetime.time],
|
||||
time: Union[float, dtm.timedelta, dtm.datetime, dtm.time],
|
||||
shift_day: bool = False,
|
||||
) -> datetime.datetime: ...
|
||||
) -> dtm.datetime: ...
|
||||
|
||||
def _parse_time_input(
|
||||
self,
|
||||
time: Union[float, datetime.timedelta, datetime.datetime, datetime.time, None],
|
||||
time: Union[float, dtm.timedelta, dtm.datetime, dtm.time, None],
|
||||
shift_day: bool = False,
|
||||
) -> Optional[datetime.datetime]:
|
||||
) -> Optional[dtm.datetime]:
|
||||
if time is None:
|
||||
return None
|
||||
if isinstance(time, (int, float)):
|
||||
return self._tz_now() + datetime.timedelta(seconds=time)
|
||||
if isinstance(time, datetime.timedelta):
|
||||
return self._tz_now() + dtm.timedelta(seconds=time)
|
||||
if isinstance(time, dtm.timedelta):
|
||||
return self._tz_now() + time
|
||||
if isinstance(time, datetime.time):
|
||||
date_time = datetime.datetime.combine(
|
||||
datetime.datetime.now(tz=time.tzinfo or self.scheduler.timezone).date(), time
|
||||
if isinstance(time, dtm.time):
|
||||
date_time = dtm.datetime.combine(
|
||||
dtm.datetime.now(tz=time.tzinfo or self.scheduler.timezone).date(), time
|
||||
)
|
||||
if date_time.tzinfo is None:
|
||||
date_time = localize(date_time, self.scheduler.timezone)
|
||||
if shift_day and date_time <= datetime.datetime.now(UTC):
|
||||
date_time += datetime.timedelta(days=1)
|
||||
if shift_day and date_time <= dtm.datetime.now(UTC):
|
||||
date_time += dtm.timedelta(days=1)
|
||||
return date_time
|
||||
return time
|
||||
|
||||
|
@ -242,7 +242,7 @@ class JobQueue(Generic[CCT]):
|
|||
def run_once(
|
||||
self,
|
||||
callback: JobCallback[CCT],
|
||||
when: Union[float, datetime.timedelta, datetime.datetime, datetime.time],
|
||||
when: Union[float, dtm.timedelta, dtm.datetime, dtm.time],
|
||||
data: Optional[object] = None,
|
||||
name: Optional[str] = None,
|
||||
chat_id: Optional[int] = None,
|
||||
|
@ -326,9 +326,9 @@ class JobQueue(Generic[CCT]):
|
|||
def run_repeating(
|
||||
self,
|
||||
callback: JobCallback[CCT],
|
||||
interval: Union[float, datetime.timedelta],
|
||||
first: Optional[Union[float, datetime.timedelta, datetime.datetime, datetime.time]] = None,
|
||||
last: Optional[Union[float, datetime.timedelta, datetime.datetime, datetime.time]] = None,
|
||||
interval: Union[float, dtm.timedelta],
|
||||
first: Optional[Union[float, dtm.timedelta, dtm.datetime, dtm.time]] = None,
|
||||
last: Optional[Union[float, dtm.timedelta, dtm.datetime, dtm.time]] = None,
|
||||
data: Optional[object] = None,
|
||||
name: Optional[str] = None,
|
||||
chat_id: Optional[int] = None,
|
||||
|
@ -433,7 +433,7 @@ class JobQueue(Generic[CCT]):
|
|||
if dt_last and dt_first and dt_last < dt_first:
|
||||
raise ValueError("'last' must not be before 'first'!")
|
||||
|
||||
if isinstance(interval, datetime.timedelta):
|
||||
if isinstance(interval, dtm.timedelta):
|
||||
interval = interval.total_seconds()
|
||||
|
||||
j = self.scheduler.add_job(
|
||||
|
@ -453,7 +453,7 @@ class JobQueue(Generic[CCT]):
|
|||
def run_monthly(
|
||||
self,
|
||||
callback: JobCallback[CCT],
|
||||
when: datetime.time,
|
||||
when: dtm.time,
|
||||
day: int,
|
||||
data: Optional[object] = None,
|
||||
name: Optional[str] = None,
|
||||
|
@ -531,7 +531,7 @@ class JobQueue(Generic[CCT]):
|
|||
def run_daily(
|
||||
self,
|
||||
callback: JobCallback[CCT],
|
||||
time: datetime.time,
|
||||
time: dtm.time,
|
||||
days: tuple[int, ...] = _ALL_DAYS,
|
||||
data: Optional[object] = None,
|
||||
name: Optional[str] = None,
|
||||
|
@ -904,7 +904,7 @@ class Job(Generic[CCT]):
|
|||
self._enabled = status
|
||||
|
||||
@property
|
||||
def next_t(self) -> Optional[datetime.datetime]:
|
||||
def next_t(self) -> Optional[dtm.datetime]:
|
||||
"""
|
||||
:class:`datetime.datetime`: Datetime for the next job execution.
|
||||
Datetime is localized according to :attr:`datetime.datetime.tzinfo`.
|
||||
|
|
|
@ -269,7 +269,7 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
|||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.get_updates`.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
.. versionchanged:: 21.9
|
||||
Accepts any :class:`collections.abc.Sequence` as input instead of just a list
|
||||
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
|
||||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
|
@ -523,7 +523,7 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
|||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.set_webhook`. Defaults to :obj:`None`.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
.. versionchanged:: 21.9
|
||||
Accepts any :class:`collections.abc.Sequence` as input instead of just a list
|
||||
max_connections (:obj:`int`, optional): Passed to
|
||||
:meth:`telegram.Bot.set_webhook`. Defaults to ``40``.
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains a class that describes a single parameter of a request to the Bot API."""
|
||||
import datetime as dtm
|
||||
import json
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional, final
|
||||
|
||||
from telegram._files.inputfile import InputFile
|
||||
|
@ -113,7 +113,7 @@ class RequestParameter:
|
|||
* if a user passes a custom enum, it's unlikely that we can actually properly handle it
|
||||
even with some special casing.
|
||||
"""
|
||||
if isinstance(value, datetime):
|
||||
if isinstance(value, dtm.datetime):
|
||||
return to_timestamp(value), []
|
||||
if isinstance(value, StringEnum):
|
||||
return value.value, []
|
||||
|
|
|
@ -699,6 +699,54 @@ class TestStickerSetWithoutRequest(StickerSetTestBase):
|
|||
monkeypatch.setattr(sticker.get_bot(), "get_file", make_assertion)
|
||||
assert await sticker.get_file()
|
||||
|
||||
async def test_delete_sticker_from_set_sticker_input(self, offline_bot, sticker, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["sticker"] == sticker.file_id
|
||||
|
||||
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
|
||||
assert await offline_bot.delete_sticker_from_set(sticker)
|
||||
|
||||
async def test_replace_sticker_in_set_sticker_input(self, offline_bot, sticker, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["old_sticker"] == sticker.file_id
|
||||
|
||||
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
|
||||
assert await offline_bot.replace_sticker_in_set(
|
||||
user_id=1, name="name", sticker="sticker", old_sticker=sticker
|
||||
)
|
||||
|
||||
async def test_set_sticker_emoji_list_sticker_input(self, offline_bot, sticker, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["sticker"] == sticker.file_id
|
||||
|
||||
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
|
||||
assert await offline_bot.set_sticker_emoji_list(sticker, ["emoji"])
|
||||
|
||||
async def test_set_sticker_mask_position_sticker_input(
|
||||
self, offline_bot, sticker, monkeypatch
|
||||
):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["sticker"] == sticker.file_id
|
||||
|
||||
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
|
||||
assert await offline_bot.set_sticker_mask_position(sticker, MaskPosition("eyes", 1, 2, 3))
|
||||
|
||||
async def test_set_sticker_position_in_set_sticker_input(
|
||||
self, offline_bot, sticker, monkeypatch
|
||||
):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["sticker"] == sticker.file_id
|
||||
|
||||
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
|
||||
assert await offline_bot.set_sticker_position_in_set(sticker, 1)
|
||||
|
||||
async def test_set_sticker_keywords_sticker_input(self, offline_bot, sticker, monkeypatch):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
return request_data.json_parameters["sticker"] == sticker.file_id
|
||||
|
||||
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
|
||||
assert await offline_bot.set_sticker_keywords(sticker, ["keyword"])
|
||||
|
||||
|
||||
@pytest.mark.xdist_group("stickerset")
|
||||
class TestStickerSetWithRequest:
|
||||
|
|
0
tests/_payment/stars/__init__.py
Normal file
0
tests/_payment/stars/__init__.py
Normal file
151
tests/_payment/stars/test_affiliateinfo.py
Normal file
151
tests/_payment/stars/test_affiliateinfo.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
#!/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 pytest
|
||||
|
||||
from telegram import AffiliateInfo, Chat, Dice, User
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def affiliate_info():
|
||||
return AffiliateInfo(
|
||||
affiliate_user=AffiliateInfoTestBase.affiliate_user,
|
||||
affiliate_chat=AffiliateInfoTestBase.affiliate_chat,
|
||||
commission_per_mille=AffiliateInfoTestBase.commission_per_mille,
|
||||
amount=AffiliateInfoTestBase.amount,
|
||||
nanostar_amount=AffiliateInfoTestBase.nanostar_amount,
|
||||
)
|
||||
|
||||
|
||||
class AffiliateInfoTestBase:
|
||||
affiliate_user = User(id=1, is_bot=True, first_name="affiliate_user", username="username")
|
||||
affiliate_chat = Chat(id=2, type="private", title="affiliate_chat")
|
||||
commission_per_mille = 13
|
||||
amount = 14
|
||||
nanostar_amount = -42
|
||||
|
||||
|
||||
class TestAffiliateInfoWithoutRequest(AffiliateInfoTestBase):
|
||||
def test_slot_behaviour(self, affiliate_info):
|
||||
inst = affiliate_info
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"affiliate_user": self.affiliate_user.to_dict(),
|
||||
"affiliate_chat": self.affiliate_chat.to_dict(),
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
"amount": self.amount,
|
||||
"nanostar_amount": self.nanostar_amount,
|
||||
}
|
||||
ai = AffiliateInfo.de_json(json_dict, offline_bot)
|
||||
assert ai.api_kwargs == {}
|
||||
assert ai.affiliate_user == self.affiliate_user
|
||||
assert ai.affiliate_chat == self.affiliate_chat
|
||||
assert ai.commission_per_mille == self.commission_per_mille
|
||||
assert ai.amount == self.amount
|
||||
assert ai.nanostar_amount == self.nanostar_amount
|
||||
|
||||
assert AffiliateInfo.de_json(None, offline_bot) is None
|
||||
assert AffiliateInfo.de_json({}, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, affiliate_info):
|
||||
ai_dict = affiliate_info.to_dict()
|
||||
|
||||
assert isinstance(ai_dict, dict)
|
||||
assert ai_dict["affiliate_user"] == affiliate_info.affiliate_user.to_dict()
|
||||
assert ai_dict["affiliate_chat"] == affiliate_info.affiliate_chat.to_dict()
|
||||
assert ai_dict["commission_per_mille"] == affiliate_info.commission_per_mille
|
||||
assert ai_dict["amount"] == affiliate_info.amount
|
||||
assert ai_dict["nanostar_amount"] == affiliate_info.nanostar_amount
|
||||
|
||||
def test_equality(self, affiliate_info, offline_bot):
|
||||
a = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
b = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
c = AffiliateInfo(
|
||||
affiliate_user=User(id=3, is_bot=True, first_name="first_name", username="username"),
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
d = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=Chat(id=3, type="private", title="title"),
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
e = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=1,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
f = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=1,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
g = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=1,
|
||||
)
|
||||
h = Dice(4, "emoji")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
assert a != f
|
||||
assert hash(a) != hash(f)
|
||||
|
||||
assert a != g
|
||||
assert hash(a) != hash(g)
|
||||
|
||||
assert a != h
|
||||
assert hash(a) != hash(h)
|
235
tests/_payment/stars/test_revenuewithdrawelstate.py
Normal file
235
tests/_payment/stars/test_revenuewithdrawelstate.py
Normal file
|
@ -0,0 +1,235 @@
|
|||
#!/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 datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
Dice,
|
||||
RevenueWithdrawalState,
|
||||
RevenueWithdrawalStateFailed,
|
||||
RevenueWithdrawalStatePending,
|
||||
RevenueWithdrawalStateSucceeded,
|
||||
)
|
||||
from telegram._utils.datetime import UTC, to_timestamp
|
||||
from telegram.constants import RevenueWithdrawalStateType
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def revenue_withdrawal_state():
|
||||
return RevenueWithdrawalState(RevenueWithdrawalStateTestBase.state)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def revenue_withdrawal_state_pending():
|
||||
return RevenueWithdrawalStatePending()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def revenue_withdrawal_state_succeeded():
|
||||
return RevenueWithdrawalStateSucceeded(
|
||||
date=RevenueWithdrawalStateTestBase.date, url=RevenueWithdrawalStateTestBase.url
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def revenue_withdrawal_state_failed():
|
||||
return RevenueWithdrawalStateFailed()
|
||||
|
||||
|
||||
class RevenueWithdrawalStateTestBase:
|
||||
state = "failed"
|
||||
date = dtm.datetime(2024, 1, 1, 0, 0, 0, 0, tzinfo=UTC)
|
||||
url = "url"
|
||||
|
||||
|
||||
class TestRevenueWithdrawalStateWithoutRequest(RevenueWithdrawalStateTestBase):
|
||||
def test_slot_behaviour(self, revenue_withdrawal_state):
|
||||
inst = revenue_withdrawal_state
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_type_enum_conversion(self):
|
||||
assert type(RevenueWithdrawalState("failed").type) is RevenueWithdrawalStateType
|
||||
assert RevenueWithdrawalState("unknown").type == "unknown"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {"type": "unknown"}
|
||||
rws = RevenueWithdrawalState.de_json(json_dict, offline_bot)
|
||||
assert rws.api_kwargs == {}
|
||||
assert rws.type == "unknown"
|
||||
|
||||
assert RevenueWithdrawalState.de_json(None, offline_bot) is None
|
||||
assert RevenueWithdrawalState.de_json({}, offline_bot) is None
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("state", "subclass"),
|
||||
[
|
||||
("pending", RevenueWithdrawalStatePending),
|
||||
("succeeded", RevenueWithdrawalStateSucceeded),
|
||||
("failed", RevenueWithdrawalStateFailed),
|
||||
],
|
||||
)
|
||||
def test_de_json_subclass(self, offline_bot, state, subclass):
|
||||
json_dict = {"type": state, "date": to_timestamp(self.date), "url": self.url}
|
||||
rws = RevenueWithdrawalState.de_json(json_dict, offline_bot)
|
||||
|
||||
assert type(rws) is subclass
|
||||
assert set(rws.api_kwargs.keys()) == {"date", "url"} - set(subclass.__slots__)
|
||||
assert rws.type == state
|
||||
|
||||
def test_to_dict(self, revenue_withdrawal_state):
|
||||
json_dict = revenue_withdrawal_state.to_dict()
|
||||
assert json_dict == {"type": self.state}
|
||||
|
||||
def test_equality(self, revenue_withdrawal_state):
|
||||
a = revenue_withdrawal_state
|
||||
b = RevenueWithdrawalState(self.state)
|
||||
c = RevenueWithdrawalState("pending")
|
||||
d = Dice(value=1, emoji="🎲")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
class TestRevenueWithdrawalStatePendingWithoutRequest(RevenueWithdrawalStateTestBase):
|
||||
def test_slot_behaviour(self, revenue_withdrawal_state_pending):
|
||||
inst = revenue_withdrawal_state_pending
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {}
|
||||
rws = RevenueWithdrawalStatePending.de_json(json_dict, offline_bot)
|
||||
assert rws.api_kwargs == {}
|
||||
assert rws.type == "pending"
|
||||
|
||||
assert RevenueWithdrawalStatePending.de_json(None, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, revenue_withdrawal_state_pending):
|
||||
json_dict = revenue_withdrawal_state_pending.to_dict()
|
||||
assert json_dict == {"type": "pending"}
|
||||
|
||||
def test_equality(self, revenue_withdrawal_state_pending):
|
||||
a = revenue_withdrawal_state_pending
|
||||
b = RevenueWithdrawalStatePending()
|
||||
c = RevenueWithdrawalState("unknown")
|
||||
d = Dice(value=1, emoji="🎲")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
class TestRevenueWithdrawalStateSucceededWithoutRequest(RevenueWithdrawalStateTestBase):
|
||||
state = RevenueWithdrawalStateType.SUCCEEDED
|
||||
|
||||
def test_slot_behaviour(self, revenue_withdrawal_state_succeeded):
|
||||
inst = revenue_withdrawal_state_succeeded
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {"date": to_timestamp(self.date), "url": self.url}
|
||||
rws = RevenueWithdrawalStateSucceeded.de_json(json_dict, offline_bot)
|
||||
assert rws.api_kwargs == {}
|
||||
assert rws.type == "succeeded"
|
||||
assert rws.date == self.date
|
||||
assert rws.url == self.url
|
||||
|
||||
assert RevenueWithdrawalStateSucceeded.de_json(None, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, revenue_withdrawal_state_succeeded):
|
||||
json_dict = revenue_withdrawal_state_succeeded.to_dict()
|
||||
assert json_dict["type"] == "succeeded"
|
||||
assert json_dict["date"] == to_timestamp(self.date)
|
||||
assert json_dict["url"] == self.url
|
||||
|
||||
def test_equality(self, revenue_withdrawal_state_succeeded):
|
||||
a = revenue_withdrawal_state_succeeded
|
||||
b = RevenueWithdrawalStateSucceeded(date=self.date, url=self.url)
|
||||
c = RevenueWithdrawalStateSucceeded(date=self.date, url="other_url")
|
||||
d = RevenueWithdrawalStateSucceeded(
|
||||
date=dtm.datetime(1900, 1, 1, 0, 0, 0, 0, tzinfo=UTC), url=self.url
|
||||
)
|
||||
e = Dice(value=1, emoji="🎲")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
|
||||
class TestRevenueWithdrawalStateFailedWithoutRequest(RevenueWithdrawalStateTestBase):
|
||||
state = RevenueWithdrawalStateType.FAILED
|
||||
|
||||
def test_slot_behaviour(self, revenue_withdrawal_state_failed):
|
||||
inst = revenue_withdrawal_state_failed
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {}
|
||||
rws = RevenueWithdrawalStateFailed.de_json(json_dict, offline_bot)
|
||||
assert rws.api_kwargs == {}
|
||||
assert rws.type == "failed"
|
||||
|
||||
assert RevenueWithdrawalStateFailed.de_json(None, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, revenue_withdrawal_state_failed):
|
||||
json_dict = revenue_withdrawal_state_failed.to_dict()
|
||||
assert json_dict == {"type": "failed"}
|
||||
|
||||
def test_equality(self, revenue_withdrawal_state_failed):
|
||||
a = revenue_withdrawal_state_failed
|
||||
b = RevenueWithdrawalStateFailed()
|
||||
c = RevenueWithdrawalState("unknown")
|
||||
d = Dice(value=1, emoji="🎲")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
207
tests/_payment/stars/test_startransactions.py
Normal file
207
tests/_payment/stars/test_startransactions.py
Normal file
|
@ -0,0 +1,207 @@
|
|||
#!/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 datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
StarTransaction,
|
||||
StarTransactions,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerUser,
|
||||
User,
|
||||
)
|
||||
from telegram._utils.datetime import UTC, from_timestamp, to_timestamp
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
def star_transaction_factory():
|
||||
return StarTransaction(
|
||||
id=StarTransactionTestBase.id,
|
||||
amount=StarTransactionTestBase.amount,
|
||||
nanostar_amount=StarTransactionTestBase.nanostar_amount,
|
||||
date=from_timestamp(StarTransactionTestBase.date),
|
||||
source=StarTransactionTestBase.source,
|
||||
receiver=StarTransactionTestBase.receiver,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def star_transaction():
|
||||
return star_transaction_factory()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def star_transactions():
|
||||
return StarTransactions(
|
||||
transactions=[
|
||||
star_transaction_factory(),
|
||||
star_transaction_factory(),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class StarTransactionTestBase:
|
||||
id = "2"
|
||||
amount = 2
|
||||
nanostar_amount = 365
|
||||
date = to_timestamp(dtm.datetime(2024, 1, 1, 0, 0, 0, 0, tzinfo=UTC))
|
||||
source = TransactionPartnerUser(
|
||||
user=User(
|
||||
id=2,
|
||||
is_bot=False,
|
||||
first_name="first_name",
|
||||
),
|
||||
)
|
||||
receiver = TransactionPartnerOther()
|
||||
|
||||
|
||||
class TestStarTransactionWithoutRequest(StarTransactionTestBase):
|
||||
def test_slot_behaviour(self, star_transaction):
|
||||
inst = star_transaction
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"id": self.id,
|
||||
"amount": self.amount,
|
||||
"nanostar_amount": self.nanostar_amount,
|
||||
"date": self.date,
|
||||
"source": self.source.to_dict(),
|
||||
"receiver": self.receiver.to_dict(),
|
||||
}
|
||||
st = StarTransaction.de_json(json_dict, offline_bot)
|
||||
st_none = StarTransaction.de_json(None, offline_bot)
|
||||
assert st.api_kwargs == {}
|
||||
assert st.id == self.id
|
||||
assert st.amount == self.amount
|
||||
assert st.nanostar_amount == self.nanostar_amount
|
||||
assert st.date == from_timestamp(self.date)
|
||||
assert st.source == self.source
|
||||
assert st.receiver == self.receiver
|
||||
assert st_none is None
|
||||
|
||||
def test_de_json_star_transaction_localization(
|
||||
self, tz_bot, offline_bot, raw_bot, star_transaction
|
||||
):
|
||||
json_dict = star_transaction.to_dict()
|
||||
st_raw = StarTransaction.de_json(json_dict, raw_bot)
|
||||
st_bot = StarTransaction.de_json(json_dict, offline_bot)
|
||||
st_tz = StarTransaction.de_json(json_dict, tz_bot)
|
||||
|
||||
# comparing utcoffsets because comparing timezones is unpredicatable
|
||||
st_offset = st_tz.date.utcoffset()
|
||||
tz_bot_offset = tz_bot.defaults.tzinfo.utcoffset(st_tz.date.replace(tzinfo=None))
|
||||
|
||||
assert st_raw.date.tzinfo == UTC
|
||||
assert st_bot.date.tzinfo == UTC
|
||||
assert st_offset == tz_bot_offset
|
||||
|
||||
def test_to_dict(self, star_transaction):
|
||||
expected_dict = {
|
||||
"id": self.id,
|
||||
"amount": self.amount,
|
||||
"nanostar_amount": self.nanostar_amount,
|
||||
"date": self.date,
|
||||
"source": self.source.to_dict(),
|
||||
"receiver": self.receiver.to_dict(),
|
||||
}
|
||||
assert star_transaction.to_dict() == expected_dict
|
||||
|
||||
def test_equality(self):
|
||||
a = StarTransaction(
|
||||
id=self.id,
|
||||
amount=self.amount,
|
||||
date=self.date,
|
||||
source=self.source,
|
||||
receiver=self.receiver,
|
||||
)
|
||||
b = StarTransaction(
|
||||
id=self.id,
|
||||
amount=self.amount,
|
||||
date=None,
|
||||
source=self.source,
|
||||
receiver=self.receiver,
|
||||
)
|
||||
c = StarTransaction(
|
||||
id="3",
|
||||
amount=3,
|
||||
date=to_timestamp(dtm.datetime.utcnow()),
|
||||
source=TransactionPartnerUser(
|
||||
user=User(
|
||||
id=3,
|
||||
is_bot=False,
|
||||
first_name="first_name",
|
||||
),
|
||||
),
|
||||
receiver=TransactionPartnerOther(),
|
||||
)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
|
||||
class StarTransactionsTestBase:
|
||||
transactions = [star_transaction_factory(), star_transaction_factory()]
|
||||
|
||||
|
||||
class TestStarTransactionsWithoutRequest(StarTransactionsTestBase):
|
||||
def test_slot_behaviour(self, star_transactions):
|
||||
inst = star_transactions
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"transactions": [t.to_dict() for t in self.transactions],
|
||||
}
|
||||
st = StarTransactions.de_json(json_dict, offline_bot)
|
||||
st_none = StarTransactions.de_json(None, offline_bot)
|
||||
assert st.api_kwargs == {}
|
||||
assert st.transactions == tuple(self.transactions)
|
||||
assert st_none is None
|
||||
|
||||
def test_to_dict(self, star_transactions):
|
||||
expected_dict = {
|
||||
"transactions": [t.to_dict() for t in self.transactions],
|
||||
}
|
||||
assert star_transactions.to_dict() == expected_dict
|
||||
|
||||
def test_equality(self, star_transaction):
|
||||
a = StarTransactions(
|
||||
transactions=self.transactions,
|
||||
)
|
||||
b = StarTransactions(
|
||||
transactions=self.transactions,
|
||||
)
|
||||
c = StarTransactions(
|
||||
transactions=[star_transaction],
|
||||
)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
469
tests/_payment/stars/test_transactionpartner.py
Normal file
469
tests/_payment/stars/test_transactionpartner.py
Normal file
|
@ -0,0 +1,469 @@
|
|||
#!/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 datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
AffiliateInfo,
|
||||
Gift,
|
||||
PaidMediaVideo,
|
||||
RevenueWithdrawalStatePending,
|
||||
Sticker,
|
||||
TransactionPartner,
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
TransactionPartnerTelegramApi,
|
||||
TransactionPartnerUser,
|
||||
User,
|
||||
Video,
|
||||
)
|
||||
from telegram.constants import TransactionPartnerType
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def transaction_partner():
|
||||
return TransactionPartner(TransactionPartnerTestBase.type)
|
||||
|
||||
|
||||
class TransactionPartnerTestBase:
|
||||
type = TransactionPartnerType.AFFILIATE_PROGRAM
|
||||
commission_per_mille = 42
|
||||
sponsor_user = User(
|
||||
id=1,
|
||||
is_bot=False,
|
||||
first_name="sponsor",
|
||||
last_name="user",
|
||||
)
|
||||
withdrawal_state = RevenueWithdrawalStatePending()
|
||||
user = User(
|
||||
id=2,
|
||||
is_bot=False,
|
||||
first_name="user",
|
||||
last_name="user",
|
||||
)
|
||||
invoice_payload = "invoice_payload"
|
||||
paid_media = (
|
||||
PaidMediaVideo(
|
||||
video=Video(
|
||||
file_id="file_id",
|
||||
file_unique_id="file_unique_id",
|
||||
width=10,
|
||||
height=10,
|
||||
duration=10,
|
||||
)
|
||||
),
|
||||
)
|
||||
paid_media_payload = "paid_media_payload"
|
||||
subscription_period = dtm.timedelta(days=1)
|
||||
gift = Gift(
|
||||
id="gift_id",
|
||||
sticker=Sticker(
|
||||
file_id="file_id",
|
||||
file_unique_id="file_unique_id",
|
||||
width=10,
|
||||
height=10,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
type="type",
|
||||
),
|
||||
total_count=42,
|
||||
remaining_count=42,
|
||||
star_count=42,
|
||||
)
|
||||
affiliate = AffiliateInfo(
|
||||
commission_per_mille=42,
|
||||
amount=42,
|
||||
)
|
||||
request_count = 42
|
||||
|
||||
|
||||
class TestTransactionPartnerWithoutRequest(TransactionPartnerTestBase):
|
||||
def test_slot_behaviour(self, transaction_partner):
|
||||
inst = transaction_partner
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_type_enum_conversion(self, transaction_partner):
|
||||
assert type(TransactionPartner("affiliate_program").type) is TransactionPartnerType
|
||||
assert TransactionPartner("unknown").type == "unknown"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
data = {"type": "unknown"}
|
||||
transaction_partner = TransactionPartner.de_json(data, offline_bot)
|
||||
assert transaction_partner.api_kwargs == {}
|
||||
assert transaction_partner.type == "unknown"
|
||||
|
||||
assert TransactionPartner.de_json(None, offline_bot) is None
|
||||
assert TransactionPartner.de_json({}, offline_bot) is None
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("tp_type", "subclass"),
|
||||
[
|
||||
("affiliate_program", TransactionPartnerAffiliateProgram),
|
||||
("fragment", TransactionPartnerFragment),
|
||||
("user", TransactionPartnerUser),
|
||||
("telegram_ads", TransactionPartnerTelegramAds),
|
||||
("telegram_api", TransactionPartnerTelegramApi),
|
||||
("other", TransactionPartnerOther),
|
||||
],
|
||||
)
|
||||
def test_subclass(self, offline_bot, tp_type, subclass):
|
||||
json_dict = {
|
||||
"type": tp_type,
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
"user": self.user.to_dict(),
|
||||
"request_count": self.request_count,
|
||||
}
|
||||
tp = TransactionPartner.de_json(json_dict, offline_bot)
|
||||
|
||||
assert type(tp) is subclass
|
||||
assert set(tp.api_kwargs.keys()) == set(json_dict.keys()) - set(subclass.__slots__) - {
|
||||
"type"
|
||||
}
|
||||
assert tp.type == tp_type
|
||||
|
||||
def test_to_dict(self, transaction_partner):
|
||||
data = transaction_partner.to_dict()
|
||||
assert data == {"type": self.type}
|
||||
|
||||
def test_equality(self, transaction_partner):
|
||||
a = transaction_partner
|
||||
b = TransactionPartner(self.type)
|
||||
c = TransactionPartner("unknown")
|
||||
d = User(id=1, is_bot=False, first_name="user", last_name="user")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def transaction_partner_affiliate_program():
|
||||
return TransactionPartnerAffiliateProgram(
|
||||
commission_per_mille=TransactionPartnerTestBase.commission_per_mille,
|
||||
sponsor_user=TransactionPartnerTestBase.sponsor_user,
|
||||
)
|
||||
|
||||
|
||||
class TestTransactionPartnerAffiliateProgramWithoutRequest(TransactionPartnerTestBase):
|
||||
type = TransactionPartnerType.AFFILIATE_PROGRAM
|
||||
|
||||
def test_slot_behaviour(self, transaction_partner_affiliate_program):
|
||||
inst = transaction_partner_affiliate_program
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
"sponsor_user": self.sponsor_user.to_dict(),
|
||||
}
|
||||
tp = TransactionPartnerAffiliateProgram.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {}
|
||||
assert tp.type == "affiliate_program"
|
||||
assert tp.commission_per_mille == self.commission_per_mille
|
||||
assert tp.sponsor_user == self.sponsor_user
|
||||
|
||||
assert TransactionPartnerAffiliateProgram.de_json(None, offline_bot) is None
|
||||
assert TransactionPartnerAffiliateProgram.de_json({}, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, transaction_partner_affiliate_program):
|
||||
json_dict = transaction_partner_affiliate_program.to_dict()
|
||||
assert json_dict["type"] == self.type
|
||||
assert json_dict["commission_per_mille"] == self.commission_per_mille
|
||||
assert json_dict["sponsor_user"] == self.sponsor_user.to_dict()
|
||||
|
||||
def test_equality(self, transaction_partner_affiliate_program):
|
||||
a = transaction_partner_affiliate_program
|
||||
b = TransactionPartnerAffiliateProgram(
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
)
|
||||
c = TransactionPartnerAffiliateProgram(
|
||||
commission_per_mille=0,
|
||||
)
|
||||
d = User(id=1, is_bot=False, first_name="user", last_name="user")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def transaction_partner_fragment():
|
||||
return TransactionPartnerFragment(
|
||||
withdrawal_state=TransactionPartnerTestBase.withdrawal_state,
|
||||
)
|
||||
|
||||
|
||||
class TestTransactionPartnerFragmentWithoutRequest(TransactionPartnerTestBase):
|
||||
type = TransactionPartnerType.FRAGMENT
|
||||
|
||||
def test_slot_behaviour(self, transaction_partner_fragment):
|
||||
inst = transaction_partner_fragment
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {"withdrawal_state": self.withdrawal_state.to_dict()}
|
||||
tp = TransactionPartnerFragment.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {}
|
||||
assert tp.type == "fragment"
|
||||
assert tp.withdrawal_state == self.withdrawal_state
|
||||
|
||||
assert TransactionPartnerFragment.de_json(None, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, transaction_partner_fragment):
|
||||
json_dict = transaction_partner_fragment.to_dict()
|
||||
assert json_dict["type"] == self.type
|
||||
assert json_dict["withdrawal_state"] == self.withdrawal_state.to_dict()
|
||||
|
||||
def test_equality(self, transaction_partner_fragment):
|
||||
a = transaction_partner_fragment
|
||||
b = TransactionPartnerFragment(withdrawal_state=self.withdrawal_state)
|
||||
c = TransactionPartnerFragment(withdrawal_state=None)
|
||||
d = User(id=1, is_bot=False, first_name="user", last_name="user")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def transaction_partner_user():
|
||||
return TransactionPartnerUser(
|
||||
user=TransactionPartnerTestBase.user,
|
||||
invoice_payload=TransactionPartnerTestBase.invoice_payload,
|
||||
paid_media=TransactionPartnerTestBase.paid_media,
|
||||
paid_media_payload=TransactionPartnerTestBase.paid_media_payload,
|
||||
subscription_period=TransactionPartnerTestBase.subscription_period,
|
||||
)
|
||||
|
||||
|
||||
class TestTransactionPartnerUserWithoutRequest(TransactionPartnerTestBase):
|
||||
type = TransactionPartnerType.USER
|
||||
|
||||
def test_slot_behaviour(self, transaction_partner_user):
|
||||
inst = transaction_partner_user
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"user": self.user.to_dict(),
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"paid_media": [pm.to_dict() for pm in self.paid_media],
|
||||
"paid_media_payload": self.paid_media_payload,
|
||||
"subscription_period": self.subscription_period.total_seconds(),
|
||||
}
|
||||
tp = TransactionPartnerUser.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {}
|
||||
assert tp.type == "user"
|
||||
assert tp.user == self.user
|
||||
assert tp.invoice_payload == self.invoice_payload
|
||||
assert tp.paid_media == self.paid_media
|
||||
assert tp.paid_media_payload == self.paid_media_payload
|
||||
assert tp.subscription_period == self.subscription_period
|
||||
|
||||
assert TransactionPartnerUser.de_json(None, offline_bot) is None
|
||||
assert TransactionPartnerUser.de_json({}, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, transaction_partner_user):
|
||||
json_dict = transaction_partner_user.to_dict()
|
||||
assert json_dict["type"] == self.type
|
||||
assert json_dict["user"] == self.user.to_dict()
|
||||
assert json_dict["invoice_payload"] == self.invoice_payload
|
||||
assert json_dict["paid_media"] == [pm.to_dict() for pm in self.paid_media]
|
||||
assert json_dict["paid_media_payload"] == self.paid_media_payload
|
||||
assert json_dict["subscription_period"] == self.subscription_period.total_seconds()
|
||||
|
||||
def test_equality(self, transaction_partner_user):
|
||||
a = transaction_partner_user
|
||||
b = TransactionPartnerUser(
|
||||
user=self.user,
|
||||
)
|
||||
c = TransactionPartnerUser(
|
||||
user=User(id=1, is_bot=False, first_name="user", last_name="user"),
|
||||
)
|
||||
d = User(id=1, is_bot=False, first_name="user", last_name="user")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def transaction_partner_other():
|
||||
return TransactionPartnerOther()
|
||||
|
||||
|
||||
class TestTransactionPartnerOtherWithoutRequest(TransactionPartnerTestBase):
|
||||
type = TransactionPartnerType.OTHER
|
||||
|
||||
def test_slot_behaviour(self, transaction_partner_other):
|
||||
inst = transaction_partner_other
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {}
|
||||
tp = TransactionPartnerOther.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {}
|
||||
assert tp.type == "other"
|
||||
|
||||
assert TransactionPartnerOther.de_json(None, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, transaction_partner_other):
|
||||
json_dict = transaction_partner_other.to_dict()
|
||||
assert json_dict == {"type": self.type}
|
||||
|
||||
def test_equality(self, transaction_partner_other):
|
||||
a = transaction_partner_other
|
||||
b = TransactionPartnerOther()
|
||||
c = TransactionPartnerOther()
|
||||
d = User(id=1, is_bot=False, first_name="user", last_name="user")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def transaction_partner_telegram_ads():
|
||||
return TransactionPartnerTelegramAds()
|
||||
|
||||
|
||||
class TestTransactionPartnerTelegramAdsWithoutRequest(TransactionPartnerTestBase):
|
||||
type = TransactionPartnerType.TELEGRAM_ADS
|
||||
|
||||
def test_slot_behaviour(self, transaction_partner_telegram_ads):
|
||||
inst = transaction_partner_telegram_ads
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {}
|
||||
tp = TransactionPartnerTelegramAds.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {}
|
||||
assert tp.type == "telegram_ads"
|
||||
|
||||
assert TransactionPartnerTelegramAds.de_json(None, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, transaction_partner_telegram_ads):
|
||||
json_dict = transaction_partner_telegram_ads.to_dict()
|
||||
assert json_dict == {"type": self.type}
|
||||
|
||||
def test_equality(self, transaction_partner_telegram_ads):
|
||||
a = transaction_partner_telegram_ads
|
||||
b = TransactionPartnerTelegramAds()
|
||||
c = TransactionPartnerTelegramAds()
|
||||
d = User(id=1, is_bot=False, first_name="user", last_name="user")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a == c
|
||||
assert hash(a) == hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def transaction_partner_telegram_api():
|
||||
return TransactionPartnerTelegramApi(
|
||||
request_count=TransactionPartnerTestBase.request_count,
|
||||
)
|
||||
|
||||
|
||||
class TestTransactionPartnerTelegramApiWithoutRequest(TransactionPartnerTestBase):
|
||||
type = TransactionPartnerType.TELEGRAM_API
|
||||
|
||||
def test_slot_behaviour(self, transaction_partner_telegram_api):
|
||||
inst = transaction_partner_telegram_api
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {"request_count": self.request_count}
|
||||
tp = TransactionPartnerTelegramApi.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {}
|
||||
assert tp.type == "telegram_api"
|
||||
assert tp.request_count == self.request_count
|
||||
|
||||
assert TransactionPartnerTelegramApi.de_json(None, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, transaction_partner_telegram_api):
|
||||
json_dict = transaction_partner_telegram_api.to_dict()
|
||||
assert json_dict["type"] == self.type
|
||||
assert json_dict["request_count"] == self.request_count
|
||||
|
||||
def test_equality(self, transaction_partner_telegram_api):
|
||||
a = transaction_partner_telegram_api
|
||||
b = TransactionPartnerTelegramApi(
|
||||
request_count=self.request_count,
|
||||
)
|
||||
c = TransactionPartnerTelegramApi(
|
||||
request_count=0,
|
||||
)
|
||||
d = User(id=1, is_bot=False, first_name="user", last_name="user")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""Provides functions to test both methods."""
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
import functools
|
||||
import inspect
|
||||
import re
|
||||
|
@ -38,6 +38,7 @@ from telegram import (
|
|||
InputTextMessageContent,
|
||||
LinkPreviewOptions,
|
||||
ReplyParameters,
|
||||
Sticker,
|
||||
TelegramObject,
|
||||
)
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
|
@ -318,6 +319,16 @@ def build_kwargs(
|
|||
kws["error_message"] = "error"
|
||||
elif name == "options":
|
||||
kws[name] = ["option1", "option2"]
|
||||
elif name in ("sticker", "old_sticker"):
|
||||
kws[name] = Sticker(
|
||||
file_id="file_id",
|
||||
file_unique_id="file_unique_id",
|
||||
width=1,
|
||||
height=1,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
type="regular",
|
||||
)
|
||||
else:
|
||||
kws[name] = True
|
||||
|
||||
|
@ -337,12 +348,10 @@ def build_kwargs(
|
|||
elif name == "until_date":
|
||||
if manually_passed_value not in [None, DEFAULT_NONE]:
|
||||
# Europe/Berlin
|
||||
kws[name] = datetime.datetime(
|
||||
2000, 1, 1, 0, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")
|
||||
)
|
||||
kws[name] = dtm.datetime(2000, 1, 1, 0, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin"))
|
||||
else:
|
||||
# naive UTC
|
||||
kws[name] = datetime.datetime(2000, 1, 1, 0)
|
||||
kws[name] = dtm.datetime(2000, 1, 1, 0)
|
||||
elif name == "reply_parameters":
|
||||
kws[name] = telegram.ReplyParameters(
|
||||
message_id=1,
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
import re
|
||||
|
||||
from telegram import Chat, Message, MessageEntity, Update, User
|
||||
|
@ -24,7 +24,7 @@ from tests.auxil.ci_bots import BOT_INFO_PROVIDER
|
|||
from tests.auxil.pytest_classes import make_bot
|
||||
|
||||
CMD_PATTERN = re.compile(r"/[\da-z_]{1,32}(?:@\w{1,32})?")
|
||||
DATE = datetime.datetime.now()
|
||||
DATE = dtm.datetime.now()
|
||||
|
||||
|
||||
def make_message(text: str, offline: bool = True, **kwargs):
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
#
|
||||
# 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
|
||||
import datetime as dtm
|
||||
|
||||
|
||||
class BasicTimezone(datetime.tzinfo):
|
||||
class BasicTimezone(dtm.tzinfo):
|
||||
def __init__(self, offset, name):
|
||||
self.offset = offset
|
||||
self.name = name
|
||||
|
@ -28,4 +28,4 @@ class BasicTimezone(datetime.tzinfo):
|
|||
return self.offset
|
||||
|
||||
def dst(self, dt):
|
||||
return datetime.timedelta(0)
|
||||
return dtm.timedelta(0)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -71,7 +71,7 @@ def false_update(request):
|
|||
|
||||
@pytest.fixture(scope="class")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
return dtm.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
|
@ -80,7 +80,7 @@ def business_connection(bot):
|
|||
id="1",
|
||||
user_chat_id=1,
|
||||
user=User(1, "name", username="user_a", is_bot=False),
|
||||
date=datetime.datetime.now(tz=UTC),
|
||||
date=dtm.datetime.now(tz=UTC),
|
||||
can_reply=True,
|
||||
is_enabled=True,
|
||||
)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -71,7 +71,7 @@ def false_update(request):
|
|||
|
||||
@pytest.fixture(scope="class")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
return dtm.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
#
|
||||
# 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 time
|
||||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
@ -181,7 +181,9 @@ class TestCallbackDataCache:
|
|||
callback_data_cache.clear_callback_data()
|
||||
|
||||
chat = Chat(1, "private")
|
||||
effective_message = Message(message_id=1, date=datetime.now(), chat=chat, reply_markup=out)
|
||||
effective_message = Message(
|
||||
message_id=1, date=dtm.datetime.now(), chat=chat, reply_markup=out
|
||||
)
|
||||
effective_message._unfreeze()
|
||||
effective_message.reply_to_message = deepcopy(effective_message)
|
||||
effective_message.pinned_message = deepcopy(effective_message)
|
||||
|
@ -374,9 +376,9 @@ class TestCallbackDataCache:
|
|||
if time_method == "time":
|
||||
cutoff = time.time()
|
||||
elif time_method == "datetime":
|
||||
cutoff = datetime.now(UTC)
|
||||
cutoff = dtm.datetime.now(UTC)
|
||||
else:
|
||||
cutoff = datetime.now(tz_bot.defaults.tzinfo).replace(tzinfo=None)
|
||||
cutoff = dtm.datetime.now(tz_bot.defaults.tzinfo).replace(tzinfo=None)
|
||||
callback_data_cache.bot = tz_bot
|
||||
time.sleep(0.1)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -72,7 +72,7 @@ def false_update(request):
|
|||
|
||||
@pytest.fixture(scope="class")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
return dtm.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
import re
|
||||
|
||||
|
@ -49,14 +49,12 @@ def update():
|
|||
0,
|
||||
Message(
|
||||
0,
|
||||
datetime.datetime.utcnow(),
|
||||
dtm.datetime.utcnow(),
|
||||
Chat(0, "private"),
|
||||
from_user=User(0, "Testuser", False),
|
||||
via_bot=User(0, "Testbot", True),
|
||||
sender_chat=Chat(0, "Channel"),
|
||||
forward_origin=MessageOriginUser(
|
||||
datetime.datetime.utcnow(), User(0, "Testuser", False)
|
||||
),
|
||||
forward_origin=MessageOriginUser(dtm.datetime.utcnow(), User(0, "Testuser", False)),
|
||||
),
|
||||
)
|
||||
update._unfreeze()
|
||||
|
@ -86,7 +84,7 @@ def base_class(request):
|
|||
|
||||
@pytest.fixture(scope="class")
|
||||
def message_origin_user():
|
||||
return MessageOriginUser(datetime.datetime.utcnow(), User(1, "TestOther", False))
|
||||
return MessageOriginUser(dtm.datetime.utcnow(), User(1, "TestOther", False))
|
||||
|
||||
|
||||
class TestFilters:
|
||||
|
@ -155,7 +153,7 @@ class TestFilters:
|
|||
not key.startswith("_")
|
||||
# exclude imported stuff
|
||||
and getattr(member, "__module__", "unknown module") == "telegram.ext.filters"
|
||||
and key != "sys"
|
||||
and key not in ("sys", "dtm")
|
||||
)
|
||||
}
|
||||
actual = set(filters.__all__)
|
||||
|
@ -616,7 +614,7 @@ class TestFilters:
|
|||
def test_filters_reply(self, update):
|
||||
another_message = Message(
|
||||
1,
|
||||
datetime.datetime.utcnow(),
|
||||
dtm.datetime.utcnow(),
|
||||
Chat(0, "private"),
|
||||
from_user=User(1, "TestOther", False),
|
||||
)
|
||||
|
@ -1107,7 +1105,7 @@ class TestFilters:
|
|||
|
||||
def test_filters_forwarded(self, update, message_origin_user):
|
||||
assert filters.FORWARDED.check_update(update)
|
||||
update.message.forward_origin = MessageOriginHiddenUser(datetime.datetime.utcnow(), 1)
|
||||
update.message.forward_origin = MessageOriginHiddenUser(dtm.datetime.utcnow(), 1)
|
||||
assert filters.FORWARDED.check_update(update)
|
||||
update.message.forward_origin = None
|
||||
assert not filters.FORWARDED.check_update(update)
|
||||
|
@ -2681,7 +2679,7 @@ class TestFilters:
|
|||
0,
|
||||
Message(
|
||||
0,
|
||||
datetime.datetime.utcnow(),
|
||||
dtm.datetime.utcnow(),
|
||||
Chat(0, "private"),
|
||||
document=Document("str", "other_str"),
|
||||
),
|
||||
|
|
|
@ -35,9 +35,7 @@ if TEST_WITH_OPT_DEPS:
|
|||
|
||||
UTC = pytz.utc
|
||||
else:
|
||||
import datetime
|
||||
|
||||
UTC = datetime.timezone.utc
|
||||
UTC = dtm.timezone.utc
|
||||
|
||||
|
||||
class CustomContext(CallbackContext):
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -74,7 +74,7 @@ def false_update(request):
|
|||
|
||||
@pytest.fixture(scope="class")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
return dtm.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -71,7 +71,7 @@ def false_update(request):
|
|||
|
||||
@pytest.fixture(scope="class")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
return dtm.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
import gzip
|
||||
import os
|
||||
import pickle
|
||||
|
@ -229,7 +229,7 @@ def pickle_files_wo_callback_data(user_data, chat_data, bot_data, conversations)
|
|||
def update(bot):
|
||||
user = User(id=321, first_name="test_user", is_bot=False)
|
||||
chat = Chat(id=123, type="group")
|
||||
message = Message(1, datetime.datetime.now(), chat, from_user=user, text="Hi there")
|
||||
message = Message(1, dtm.datetime.now(), chat, from_user=user, text="Hi there")
|
||||
message.set_bot(bot)
|
||||
return Update(0, message=message)
|
||||
|
||||
|
@ -289,7 +289,7 @@ class TestPicklePersistence:
|
|||
|
||||
async def test_pickle_behaviour_with_slots(self, pickle_persistence):
|
||||
bot_data = await pickle_persistence.get_bot_data()
|
||||
bot_data["message"] = Message(3, datetime.datetime.now(), Chat(2, type="supergroup"))
|
||||
bot_data["message"] = Message(3, dtm.datetime.now(), Chat(2, type="supergroup"))
|
||||
await pickle_persistence.update_bot_data(bot_data)
|
||||
retrieved = await pickle_persistence.get_bot_data()
|
||||
assert retrieved == bot_data
|
||||
|
|
|
@ -22,10 +22,10 @@ We mostly test on directly on AIORateLimiter here, b/c BaseRateLimiter doesn't c
|
|||
notable
|
||||
"""
|
||||
import asyncio
|
||||
import datetime as dtm
|
||||
import json
|
||||
import platform
|
||||
import time
|
||||
from datetime import datetime
|
||||
from http import HTTPStatus
|
||||
|
||||
import pytest
|
||||
|
@ -181,7 +181,7 @@ class TestAIORateLimiter:
|
|||
{
|
||||
"ok": True,
|
||||
"result": Message(
|
||||
message_id=1, date=datetime.now(), chat=Chat(1, "chat")
|
||||
message_id=1, date=dtm.datetime.now(), chat=Chat(1, "chat")
|
||||
).to_dict(),
|
||||
}
|
||||
).encode(),
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
from collections.abc import Sequence
|
||||
|
||||
import pytest
|
||||
|
@ -82,14 +82,14 @@ class TestRequestParameterWithoutRequest:
|
|||
({1: 1.0}, {1: 1.0}),
|
||||
(ChatType.PRIVATE, "private"),
|
||||
(MessageEntity("type", 1, 1), {"type": "type", "offset": 1, "length": 1}),
|
||||
(datetime.datetime(2019, 11, 11, 0, 26, 16, 10**5), 1573431976),
|
||||
(dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5), 1573431976),
|
||||
(
|
||||
[
|
||||
True,
|
||||
"str",
|
||||
MessageEntity("type", 1, 1),
|
||||
ChatType.PRIVATE,
|
||||
datetime.datetime(2019, 11, 11, 0, 26, 16, 10**5),
|
||||
dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5),
|
||||
],
|
||||
[True, "str", {"type": "type", "offset": 1, "length": 1}, "private", 1573431976],
|
||||
),
|
||||
|
|
|
@ -16,7 +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/].
|
||||
from datetime import date
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -72,10 +72,10 @@ class TestBirthdateWithoutRequest(BirthdateTestBase):
|
|||
assert hash(bd1) != hash(bd4)
|
||||
|
||||
def test_to_date(self, birthdate):
|
||||
assert isinstance(birthdate.to_date(), date)
|
||||
assert birthdate.to_date() == date(self.year, self.month, self.day)
|
||||
assert isinstance(birthdate.to_date(), dtm.date)
|
||||
assert birthdate.to_date() == dtm.date(self.year, self.month, self.day)
|
||||
new_bd = birthdate.to_date(2023)
|
||||
assert new_bd == date(2023, self.month, self.day)
|
||||
assert new_bd == dtm.date(2023, self.month, self.day)
|
||||
|
||||
def test_to_date_no_year(self):
|
||||
bd = Birthdate(1, 1)
|
||||
|
|
|
@ -16,7 +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/].
|
||||
from datetime import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -40,7 +40,7 @@ class BusinessTestBase:
|
|||
id_ = "123"
|
||||
user = User(123, "test_user", False)
|
||||
user_chat_id = 123
|
||||
date = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
date = dtm.datetime.now(tz=UTC).replace(microsecond=0)
|
||||
can_reply = True
|
||||
is_enabled = True
|
||||
message_ids = (123, 321)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
from datetime import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -58,7 +58,9 @@ class CallbackQueryTestBase:
|
|||
id_ = "id"
|
||||
from_user = User(1, "test_user", False)
|
||||
chat_instance = "chat_instance"
|
||||
message = Message(3, datetime.utcnow(), Chat(4, "private"), from_user=User(5, "bot", False))
|
||||
message = Message(
|
||||
3, dtm.datetime.utcnow(), Chat(4, "private"), from_user=User(5, "bot", False)
|
||||
)
|
||||
data = "data"
|
||||
inline_message_id = "inline_message_id"
|
||||
game_short_name = "the_game"
|
||||
|
|
|
@ -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
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
from copy import deepcopy
|
||||
|
||||
|
@ -48,7 +48,7 @@ class ChatBoostDefaults:
|
|||
is_unclaimed = False
|
||||
chat = Chat(1, "group")
|
||||
user = User(1, "user", False)
|
||||
date = to_timestamp(datetime.datetime.utcnow())
|
||||
date = to_timestamp(dtm.datetime.utcnow())
|
||||
default_source = ChatBoostSourcePremium(user)
|
||||
prize_star_count = 99
|
||||
|
||||
|
@ -148,7 +148,7 @@ def iter_args(
|
|||
if param.name in ignored:
|
||||
continue
|
||||
inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name)
|
||||
if isinstance(json_at, datetime.datetime): # Convert datetime to int
|
||||
if isinstance(json_at, dtm.datetime): # Convert dtm to int
|
||||
json_at = to_timestamp(json_at)
|
||||
if (
|
||||
param.default is not inspect.Parameter.empty and include_optional
|
||||
|
@ -275,8 +275,8 @@ class TestChatBoostWithoutRequest(ChatBoostDefaults):
|
|||
cb = ChatBoost.de_json(json_dict, offline_bot)
|
||||
|
||||
assert isinstance(cb, ChatBoost)
|
||||
assert isinstance(cb.add_date, datetime.datetime)
|
||||
assert isinstance(cb.expiration_date, datetime.datetime)
|
||||
assert isinstance(cb.add_date, dtm.datetime)
|
||||
assert isinstance(cb.expiration_date, dtm.datetime)
|
||||
assert isinstance(cb.source, ChatBoostSource)
|
||||
with cb._unfrozen():
|
||||
cb.add_date = to_timestamp(cb.add_date)
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -116,7 +116,7 @@ class ChatFullInfoTestBase:
|
|||
is_forum = True
|
||||
active_usernames = ["These", "Are", "Usernames!"]
|
||||
emoji_status_custom_emoji_id = "VeryUniqueCustomEmojiID"
|
||||
emoji_status_expiration_date = datetime.datetime.now(tz=UTC).replace(microsecond=0)
|
||||
emoji_status_expiration_date = dtm.datetime.now(tz=UTC).replace(microsecond=0)
|
||||
has_aggressive_anti_spam_enabled = True
|
||||
has_hidden_members = True
|
||||
available_reactions = [
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -52,7 +52,7 @@ class ChatInviteLinkTestBase:
|
|||
creates_join_request = False
|
||||
primary = True
|
||||
revoked = False
|
||||
expire_date = datetime.datetime.now(datetime.timezone.utc)
|
||||
expire_date = dtm.datetime.now(dtm.timezone.utc)
|
||||
member_limit = 42
|
||||
name = "LinkName"
|
||||
pending_join_request_count = 42
|
||||
|
@ -107,7 +107,7 @@ class TestChatInviteLinkWithoutRequest(ChatInviteLinkTestBase):
|
|||
assert invite_link.creates_join_request == self.creates_join_request
|
||||
assert invite_link.is_primary == self.primary
|
||||
assert invite_link.is_revoked == self.revoked
|
||||
assert abs(invite_link.expire_date - self.expire_date) < datetime.timedelta(seconds=1)
|
||||
assert abs(invite_link.expire_date - self.expire_date) < dtm.timedelta(seconds=1)
|
||||
assert to_timestamp(invite_link.expire_date) == to_timestamp(self.expire_date)
|
||||
assert invite_link.member_limit == self.member_limit
|
||||
assert invite_link.name == self.name
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -32,7 +32,7 @@ from tests.auxil.slots import mro_slots
|
|||
|
||||
@pytest.fixture(scope="module")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
return dtm.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
@ -82,7 +82,7 @@ class TestChatJoinRequestWithoutRequest(ChatJoinRequestTestBase):
|
|||
|
||||
assert chat_join_request.chat == self.chat
|
||||
assert chat_join_request.from_user == self.from_user
|
||||
assert abs(chat_join_request.date - time) < datetime.timedelta(seconds=1)
|
||||
assert abs(chat_join_request.date - time) < dtm.timedelta(seconds=1)
|
||||
assert to_timestamp(chat_join_request.date) == to_timestamp(time)
|
||||
assert chat_join_request.user_chat_id == self.from_user.id
|
||||
|
||||
|
@ -92,7 +92,7 @@ class TestChatJoinRequestWithoutRequest(ChatJoinRequestTestBase):
|
|||
|
||||
assert chat_join_request.chat == self.chat
|
||||
assert chat_join_request.from_user == self.from_user
|
||||
assert abs(chat_join_request.date - time) < datetime.timedelta(seconds=1)
|
||||
assert abs(chat_join_request.date - time) < dtm.timedelta(seconds=1)
|
||||
assert to_timestamp(chat_join_request.date) == to_timestamp(time)
|
||||
assert chat_join_request.user_chat_id == self.from_user.id
|
||||
assert chat_join_request.bio == self.bio
|
||||
|
@ -133,9 +133,7 @@ class TestChatJoinRequestWithoutRequest(ChatJoinRequestTestBase):
|
|||
a = chat_join_request
|
||||
b = ChatJoinRequest(self.chat, self.from_user, time, self.from_user.id)
|
||||
c = ChatJoinRequest(self.chat, self.from_user, time, self.from_user.id, bio="bio")
|
||||
d = ChatJoinRequest(
|
||||
self.chat, self.from_user, time + datetime.timedelta(1), self.from_user.id
|
||||
)
|
||||
d = ChatJoinRequest(self.chat, self.from_user, time + dtm.timedelta(1), self.from_user.id)
|
||||
e = ChatJoinRequest(self.chat, User(-1, "last_name", True), time, -1)
|
||||
f = User(456, "", False)
|
||||
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
from copy import deepcopy
|
||||
|
||||
|
@ -43,7 +43,7 @@ class CMDefaults:
|
|||
user = User(1, "First name", False)
|
||||
custom_title: str = "PTB"
|
||||
is_anonymous: bool = True
|
||||
until_date: datetime.datetime = to_timestamp(datetime.datetime.utcnow())
|
||||
until_date: dtm.datetime = to_timestamp(dtm.datetime.utcnow())
|
||||
can_be_edited: bool = False
|
||||
can_change_info: bool = True
|
||||
can_post_messages: bool = True
|
||||
|
@ -173,7 +173,7 @@ def iter_args(instance: ChatMember, de_json_inst: ChatMember, include_optional:
|
|||
if param.name in ignored:
|
||||
continue
|
||||
inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name)
|
||||
if isinstance(json_at, datetime.datetime): # Convert datetime to int
|
||||
if isinstance(json_at, dtm.datetime): # Convert dtm to int
|
||||
json_at = to_timestamp(json_at)
|
||||
if (
|
||||
param.default is not inspect.Parameter.empty and include_optional
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
|
||||
import pytest
|
||||
|
@ -72,7 +72,7 @@ def new_chat_member(user):
|
|||
|
||||
@pytest.fixture(scope="module")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
return dtm.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
@ -115,7 +115,7 @@ class TestChatMemberUpdatedWithoutRequest(ChatMemberUpdatedTestBase):
|
|||
|
||||
assert chat_member_updated.chat == chat
|
||||
assert chat_member_updated.from_user == user
|
||||
assert abs(chat_member_updated.date - time) < datetime.timedelta(seconds=1)
|
||||
assert abs(chat_member_updated.date - time) < dtm.timedelta(seconds=1)
|
||||
assert to_timestamp(chat_member_updated.date) == to_timestamp(time)
|
||||
assert chat_member_updated.old_chat_member == old_chat_member
|
||||
assert chat_member_updated.new_chat_member == new_chat_member
|
||||
|
@ -141,7 +141,7 @@ class TestChatMemberUpdatedWithoutRequest(ChatMemberUpdatedTestBase):
|
|||
|
||||
assert chat_member_updated.chat == chat
|
||||
assert chat_member_updated.from_user == user
|
||||
assert abs(chat_member_updated.date - time) < datetime.timedelta(seconds=1)
|
||||
assert abs(chat_member_updated.date - time) < dtm.timedelta(seconds=1)
|
||||
assert to_timestamp(chat_member_updated.date) == to_timestamp(time)
|
||||
assert chat_member_updated.old_chat_member == old_chat_member
|
||||
assert chat_member_updated.new_chat_member == new_chat_member
|
||||
|
@ -221,7 +221,7 @@ class TestChatMemberUpdatedWithoutRequest(ChatMemberUpdatedTestBase):
|
|||
c = ChatMemberUpdated(
|
||||
Chat(1, "chat"),
|
||||
User(1, "", False),
|
||||
time + datetime.timedelta(hours=1),
|
||||
time + dtm.timedelta(hours=1),
|
||||
old_chat_member,
|
||||
new_chat_member,
|
||||
)
|
||||
|
@ -264,7 +264,7 @@ class TestChatMemberUpdatedWithoutRequest(ChatMemberUpdatedTestBase):
|
|||
old_chat_member = ChatMember(user, "old_status")
|
||||
new_chat_member = ChatMember(user, "new_status")
|
||||
chat_member_updated = ChatMemberUpdated(
|
||||
chat, user, datetime.datetime.utcnow(), old_chat_member, new_chat_member
|
||||
chat, user, dtm.datetime.utcnow(), old_chat_member, new_chat_member
|
||||
)
|
||||
assert chat_member_updated.difference() == {"status": ("old_status", "new_status")}
|
||||
|
||||
|
@ -273,7 +273,7 @@ class TestChatMemberUpdatedWithoutRequest(ChatMemberUpdatedTestBase):
|
|||
new_user = User(1, "First name", False, last_name="last name")
|
||||
new_chat_member = ChatMember(new_user, "new_status")
|
||||
chat_member_updated = ChatMemberUpdated(
|
||||
chat, user, datetime.datetime.utcnow(), old_chat_member, new_chat_member
|
||||
chat, user, dtm.datetime.utcnow(), old_chat_member, new_chat_member
|
||||
)
|
||||
assert chat_member_updated.difference() == {
|
||||
"status": ("old_status", "new_status"),
|
||||
|
@ -321,22 +321,22 @@ class TestChatMemberUpdatedWithoutRequest(ChatMemberUpdatedTestBase):
|
|||
can_post_stories=True,
|
||||
)
|
||||
chat_member_updated = ChatMemberUpdated(
|
||||
chat, user, datetime.datetime.utcnow(), old_chat_member, new_chat_member
|
||||
chat, user, dtm.datetime.utcnow(), old_chat_member, new_chat_member
|
||||
)
|
||||
assert chat_member_updated.difference() == {optional_attribute: (old_value, new_value)}
|
||||
|
||||
def test_difference_different_classes(self, user, chat):
|
||||
old_chat_member = ChatMemberOwner(user=user, is_anonymous=False)
|
||||
new_chat_member = ChatMemberBanned(user=user, until_date=datetime.datetime(2021, 1, 1))
|
||||
new_chat_member = ChatMemberBanned(user=user, until_date=dtm.datetime(2021, 1, 1))
|
||||
chat_member_updated = ChatMemberUpdated(
|
||||
chat=chat,
|
||||
from_user=user,
|
||||
date=datetime.datetime.utcnow(),
|
||||
date=dtm.datetime.utcnow(),
|
||||
old_chat_member=old_chat_member,
|
||||
new_chat_member=new_chat_member,
|
||||
)
|
||||
diff = chat_member_updated.difference()
|
||||
assert diff.pop("is_anonymous") == (False, None)
|
||||
assert diff.pop("until_date") == (None, datetime.datetime(2021, 1, 1))
|
||||
assert diff.pop("until_date") == (None, dtm.datetime(2021, 1, 1))
|
||||
assert diff.pop("status") == (ChatMember.OWNER, ChatMember.BANNED)
|
||||
assert diff == {}
|
||||
|
|
|
@ -58,7 +58,7 @@ class TestConstantsWithoutRequest:
|
|||
not key.startswith("_")
|
||||
# exclude imported stuff
|
||||
and getattr(member, "__module__", "telegram.constants") == "telegram.constants"
|
||||
and key not in ("sys", "datetime")
|
||||
and key not in ("sys", "dtm")
|
||||
)
|
||||
}
|
||||
actual = set(constants.__all__)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -243,7 +243,7 @@ class TestForumMethodsWithRequest:
|
|||
async def test_edit_general_forum_topic(self, bot, forum_group_id):
|
||||
result = await bot.edit_general_forum_topic(
|
||||
chat_id=forum_group_id,
|
||||
name=f"GENERAL_{datetime.datetime.now().timestamp()}",
|
||||
name=f"GENERAL_{dtm.datetime.now().timestamp()}",
|
||||
)
|
||||
assert result is True, "Failed to edit general forum topic"
|
||||
# no way of checking the edited name, just the boolean result
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
#
|
||||
# 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 contextlib
|
||||
import datetime as dtm
|
||||
from copy import copy
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -112,10 +113,10 @@ def message(bot):
|
|||
params=[
|
||||
{
|
||||
"reply_to_message": Message(
|
||||
50, datetime.utcnow(), Chat(13, "channel"), User(9, "i", False)
|
||||
50, dtm.datetime.utcnow(), Chat(13, "channel"), User(9, "i", False)
|
||||
)
|
||||
},
|
||||
{"edit_date": datetime.utcnow()},
|
||||
{"edit_date": dtm.datetime.utcnow()},
|
||||
{
|
||||
"text": "a text message",
|
||||
"entities": [MessageEntity("bold", 10, 4), MessageEntity("italic", 16, 7)],
|
||||
|
@ -161,7 +162,7 @@ def message(bot):
|
|||
{"migrate_from_chat_id": -54321},
|
||||
{
|
||||
"pinned_message": Message(
|
||||
7, datetime.utcnow(), Chat(13, "channel"), User(9, "i", False)
|
||||
7, dtm.datetime.utcnow(), Chat(13, "channel"), User(9, "i", False)
|
||||
)
|
||||
},
|
||||
{"invoice": Invoice("my invoice", "invoice", "start", "EUR", 243)},
|
||||
|
@ -210,7 +211,7 @@ def message(bot):
|
|||
User(1, "John", False), User(2, "Doe", False), 42
|
||||
)
|
||||
},
|
||||
{"video_chat_scheduled": VideoChatScheduled(datetime.utcnow())},
|
||||
{"video_chat_scheduled": VideoChatScheduled(dtm.datetime.utcnow())},
|
||||
{"video_chat_started": VideoChatStarted()},
|
||||
{"video_chat_ended": VideoChatEnded(100)},
|
||||
{
|
||||
|
@ -234,7 +235,7 @@ def message(bot):
|
|||
{
|
||||
"giveaway": Giveaway(
|
||||
chats=[Chat(1, Chat.SUPERGROUP)],
|
||||
winners_selection_date=datetime.utcnow().replace(microsecond=0),
|
||||
winners_selection_date=dtm.datetime.utcnow().replace(microsecond=0),
|
||||
winner_count=5,
|
||||
)
|
||||
},
|
||||
|
@ -243,7 +244,7 @@ def message(bot):
|
|||
"giveaway_winners": GiveawayWinners(
|
||||
chat=Chat(1, Chat.CHANNEL),
|
||||
giveaway_message_id=123456789,
|
||||
winners_selection_date=datetime.utcnow().replace(microsecond=0),
|
||||
winners_selection_date=dtm.datetime.utcnow().replace(microsecond=0),
|
||||
winner_count=42,
|
||||
winners=[User(1, "user1", False), User(2, "user2", False)],
|
||||
)
|
||||
|
@ -266,11 +267,11 @@ def message(bot):
|
|||
},
|
||||
{
|
||||
"external_reply": ExternalReplyInfo(
|
||||
MessageOriginChat(datetime.utcnow(), Chat(1, Chat.PRIVATE))
|
||||
MessageOriginChat(dtm.datetime.utcnow(), Chat(1, Chat.PRIVATE))
|
||||
)
|
||||
},
|
||||
{"quote": TextQuote("a text quote", 1)},
|
||||
{"forward_origin": MessageOriginChat(datetime.utcnow(), Chat(1, Chat.PRIVATE))},
|
||||
{"forward_origin": MessageOriginChat(dtm.datetime.utcnow(), Chat(1, Chat.PRIVATE))},
|
||||
{"reply_to_story": Story(Chat(1, Chat.PRIVATE), 0)},
|
||||
{"boost_added": ChatBoostAdded(100)},
|
||||
{"sender_boost_count": 1},
|
||||
|
@ -372,7 +373,7 @@ def message_params(bot, request):
|
|||
class MessageTestBase:
|
||||
id_ = 1
|
||||
from_user = User(2, "testuser", False)
|
||||
date = datetime.utcnow()
|
||||
date = dtm.datetime.utcnow()
|
||||
chat = Chat(3, "private")
|
||||
test_entities = [
|
||||
{"length": 4, "offset": 10, "type": "bold"},
|
||||
|
@ -591,9 +592,9 @@ class TestMessageWithoutRequest(MessageTestBase):
|
|||
json_dict = {
|
||||
"message_id": 12,
|
||||
"from_user": None,
|
||||
"date": int(datetime.now().timestamp()),
|
||||
"date": int(dtm.datetime.now().timestamp()),
|
||||
"chat": None,
|
||||
"edit_date": int(datetime.now().timestamp()),
|
||||
"edit_date": int(dtm.datetime.now().timestamp()),
|
||||
}
|
||||
|
||||
message_raw = Message.de_json(json_dict, raw_bot)
|
||||
|
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
from copy import deepcopy
|
||||
|
||||
|
@ -39,7 +39,7 @@ ignored = ["self", "api_kwargs"]
|
|||
|
||||
|
||||
class MODefaults:
|
||||
date: datetime.datetime = to_timestamp(datetime.datetime.utcnow())
|
||||
date: dtm.datetime = to_timestamp(dtm.datetime.utcnow())
|
||||
chat = Chat(1, Chat.CHANNEL)
|
||||
message_id = 123
|
||||
author_signautre = "PTB"
|
||||
|
@ -106,7 +106,7 @@ def iter_args(
|
|||
if param.name in ignored:
|
||||
continue
|
||||
inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name)
|
||||
if isinstance(json_at, datetime.datetime): # Convert datetime to int
|
||||
if isinstance(json_at, dtm.datetime): # Convert datetime to int
|
||||
json_at = to_timestamp(json_at)
|
||||
if (
|
||||
param.default is not inspect.Parameter.empty and include_optional
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
match the official API. It also checks if the type annotations are correct and if the parameters
|
||||
are required or not."""
|
||||
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
import logging
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime, timedelta
|
||||
from types import FunctionType
|
||||
from typing import Any
|
||||
|
||||
|
@ -38,6 +38,7 @@ from tests.test_official.helpers import (
|
|||
_get_params_base,
|
||||
_unionizer,
|
||||
cached_type_hints,
|
||||
extract_mappings,
|
||||
resolve_forward_refs_in_type,
|
||||
wrap_with_none,
|
||||
)
|
||||
|
@ -144,7 +145,7 @@ def check_param_type(
|
|||
)
|
||||
|
||||
# CHECKING:
|
||||
# Each branch manipulates the `mapped_type` (except for 4) ) to match the `ptb_annotation`.
|
||||
# Each branch manipulates the `mapped_type` (except for 5) ) to match the `ptb_annotation`.
|
||||
|
||||
# 1) HANDLING ARRAY TYPES:
|
||||
# Now let's do the checking, starting with "Array of ..." types.
|
||||
|
@ -174,9 +175,11 @@ def check_param_type(
|
|||
|
||||
# 2) HANDLING OTHER TYPES:
|
||||
# Special case for send_* methods where we accept more types than the official API:
|
||||
elif ptb_param.name in PTCE.ADDITIONAL_TYPES and obj.__name__.startswith("send"):
|
||||
log("Checking that `%s` has an additional argument!\n", ptb_param.name)
|
||||
mapped_type = mapped_type | PTCE.ADDITIONAL_TYPES[ptb_param.name]
|
||||
elif additional_types := extract_mappings(PTCE.ADDITIONAL_TYPES, obj, ptb_param.name):
|
||||
log("Checking that `%s` accepts additional types for some parameters!\n", obj.__name__)
|
||||
for at in additional_types:
|
||||
log("Checking that `%s` is an additional type for `%s`!\n", at, ptb_param.name)
|
||||
mapped_type = mapped_type | at
|
||||
|
||||
# 3) HANDLING DATETIMES:
|
||||
elif (
|
||||
|
@ -190,7 +193,7 @@ def check_param_type(
|
|||
if ptb_param.name in PTCE.DATETIME_EXCEPTIONS:
|
||||
return True, mapped_type
|
||||
# If it's a class, we only accept datetime as the parameter
|
||||
mapped_type = datetime if is_class else mapped_type | datetime
|
||||
mapped_type = dtm.datetime if is_class else mapped_type | dtm.datetime
|
||||
|
||||
# 4) HANDLING TIMEDELTA:
|
||||
elif re.search(TIMEDELTA_REGEX, ptb_param.name) and obj.__name__ in (
|
||||
|
@ -201,15 +204,14 @@ def check_param_type(
|
|||
# and `create_invoice_link`.
|
||||
# See https://github.com/python-telegram-bot/python-telegram-bot/issues/4575
|
||||
log("Checking that `%s` is a timedelta!\n", ptb_param.name)
|
||||
mapped_type = timedelta if is_class else mapped_type | timedelta
|
||||
mapped_type = dtm.timedelta if is_class else mapped_type | dtm.timedelta
|
||||
|
||||
# 5) COMPLEX TYPES:
|
||||
# Some types are too complicated, so we replace our annotation with a simpler type:
|
||||
elif any(ptb_param.name in key for key in PTCE.COMPLEX_TYPES):
|
||||
elif overrides := extract_mappings(PTCE.COMPLEX_TYPES, obj, ptb_param.name):
|
||||
exception_type = overrides[0]
|
||||
log("Converting `%s` to a simpler type!\n", ptb_param.name)
|
||||
for (param_name, is_expected_class), exception_type in PTCE.COMPLEX_TYPES.items():
|
||||
if ptb_param.name == param_name and is_class is is_expected_class:
|
||||
ptb_annotation = wrap_with_none(tg_parameter, exception_type, obj)
|
||||
ptb_annotation = wrap_with_none(tg_parameter, exception_type, obj)
|
||||
|
||||
# 6) HANDLING DEFAULTS PARAMETERS:
|
||||
# Classes whose parameters are all ODVInput should be converted and checked.
|
||||
|
|
|
@ -35,16 +35,25 @@ GLOBALLY_IGNORED_PARAMETERS = {
|
|||
|
||||
class ParamTypeCheckingExceptions:
|
||||
# Types for certain parameters accepted by PTB but not in the official API
|
||||
# structure: method/class_name/regex: {param_name/regex: type}
|
||||
ADDITIONAL_TYPES = {
|
||||
"photo": PhotoSize,
|
||||
"video": Video,
|
||||
"video_note": VideoNote,
|
||||
"audio": Audio,
|
||||
"document": Document,
|
||||
"animation": Animation,
|
||||
"voice": Voice,
|
||||
"sticker": Sticker,
|
||||
"gift_id": Gift,
|
||||
r"send_\w*": {
|
||||
"photo$": PhotoSize,
|
||||
"video$": Video,
|
||||
"video_note": VideoNote,
|
||||
"audio": Audio,
|
||||
"document": Document,
|
||||
"animation": Animation,
|
||||
"voice": Voice,
|
||||
"sticker": Sticker,
|
||||
"gift_id": Gift,
|
||||
},
|
||||
"(delete|set)_sticker.*": {
|
||||
"sticker$": Sticker,
|
||||
},
|
||||
"replace_sticker_in_set": {
|
||||
"old_sticker$": Sticker,
|
||||
},
|
||||
}
|
||||
|
||||
# TODO: Look into merging this with COMPLEX_TYPES
|
||||
|
@ -61,19 +70,29 @@ class ParamTypeCheckingExceptions:
|
|||
}
|
||||
|
||||
# Special cases for other parameters that accept more types than the official API, and are
|
||||
# too complex to compare/predict with official API:
|
||||
# too complex to compare/predict with official API
|
||||
# structure: class/method_name: {param_name: reduced form of annotation}
|
||||
COMPLEX_TYPES = (
|
||||
{ # (param_name, is_class (i.e appears in a class?)): reduced form of annotation
|
||||
("correct_option_id", False): int, # actual: Literal
|
||||
("file_id", False): str, # actual: Union[str, objs_with_file_id_attr]
|
||||
("invite_link", False): str, # actual: Union[str, ChatInviteLink]
|
||||
("provider_data", False): str, # actual: Union[str, obj]
|
||||
("callback_data", True): str, # actual: Union[str, obj]
|
||||
("media", True): str, # actual: Union[str, InputMedia*, FileInput]
|
||||
(
|
||||
"data",
|
||||
True,
|
||||
): str, # actual: Union[IdDocumentData, PersonalDetails, ResidentialAddress]
|
||||
"send_poll": {"correct_option_id": int}, # actual: Literal
|
||||
"get_file": {
|
||||
"file_id": str, # actual: Union[str, objs_with_file_id_attr]
|
||||
},
|
||||
r"\w+invite_link": {
|
||||
"invite_link": str, # actual: Union[str, ChatInviteLink]
|
||||
},
|
||||
"send_invoice|create_invoice_link": {
|
||||
"provider_data": str, # actual: Union[str, obj]
|
||||
},
|
||||
"InlineKeyboardButton": {
|
||||
"callback_data": str, # actual: Union[str, obj]
|
||||
},
|
||||
"Input(Paid)?Media.*": {
|
||||
"media": str, # actual: Union[str, InputMedia*, FileInput]
|
||||
},
|
||||
"EncryptedPassportElement": {
|
||||
"data": str, # actual: Union[IdDocumentData, PersonalDetails, ResidentialAddress]
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
import functools
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Any, _eval_type, get_type_hints
|
||||
from typing import TYPE_CHECKING, Any, Optional, TypeVar, _eval_type, get_type_hints
|
||||
|
||||
from bs4 import PageElement, Tag
|
||||
|
||||
|
@ -110,3 +110,22 @@ def cached_type_hints(obj: Any, is_class: bool) -> dict[str, Any]:
|
|||
def resolve_forward_refs_in_type(obj: type) -> type:
|
||||
"""Resolves forward references in a type hint."""
|
||||
return _eval_type(obj, localns=tg_objects, globalns=None)
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def extract_mappings(
|
||||
exceptions: dict[str, dict[str, T]], obj: object, param_name: str
|
||||
) -> Optional[list[T]]:
|
||||
mappings = (
|
||||
mapping for pattern, mapping in exceptions.items() if (re.match(pattern, obj.__name__))
|
||||
)
|
||||
out = [
|
||||
value
|
||||
for mapping in mappings
|
||||
for key, value in mapping.items()
|
||||
if re.match(key, param_name)
|
||||
]
|
||||
|
||||
return None or out
|
||||
|
|
|
@ -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/].
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import datetime as dtm
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -297,7 +297,7 @@ class PollTestBase:
|
|||
).decode("unicode-escape")
|
||||
explanation_entities = [MessageEntity(13, 17, MessageEntity.URL)]
|
||||
open_period = 42
|
||||
close_date = datetime.now(timezone.utc)
|
||||
close_date = dtm.datetime.now(dtm.timezone.utc)
|
||||
question_entities = [
|
||||
MessageEntity(MessageEntity.BOLD, 0, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 5, 8),
|
||||
|
@ -339,7 +339,7 @@ class TestPollWithoutRequest(PollTestBase):
|
|||
assert poll.explanation == self.explanation
|
||||
assert poll.explanation_entities == tuple(self.explanation_entities)
|
||||
assert poll.open_period == self.open_period
|
||||
assert abs(poll.close_date - self.close_date) < timedelta(seconds=1)
|
||||
assert abs(poll.close_date - self.close_date) < dtm.timedelta(seconds=1)
|
||||
assert to_timestamp(poll.close_date) == to_timestamp(self.close_date)
|
||||
assert poll.question_entities == tuple(self.question_entities)
|
||||
|
||||
|
|
|
@ -1,849 +0,0 @@
|
|||
#!/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 datetime
|
||||
from collections.abc import Sequence
|
||||
from copy import deepcopy
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
Chat,
|
||||
Dice,
|
||||
Gift,
|
||||
PaidMediaPhoto,
|
||||
PhotoSize,
|
||||
RevenueWithdrawalState,
|
||||
RevenueWithdrawalStateFailed,
|
||||
RevenueWithdrawalStatePending,
|
||||
RevenueWithdrawalStateSucceeded,
|
||||
StarTransaction,
|
||||
StarTransactions,
|
||||
Sticker,
|
||||
TelegramObject,
|
||||
TransactionPartner,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
TransactionPartnerTelegramApi,
|
||||
TransactionPartnerUser,
|
||||
User,
|
||||
)
|
||||
from telegram._payment.stars import AffiliateInfo, TransactionPartnerAffiliateProgram
|
||||
from telegram._utils.datetime import UTC, from_timestamp, to_timestamp
|
||||
from telegram.constants import RevenueWithdrawalStateType, TransactionPartnerType
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
def withdrawal_state_succeeded():
|
||||
return RevenueWithdrawalStateSucceeded(
|
||||
date=datetime.datetime(2024, 1, 1, 0, 0, 0, 0, tzinfo=UTC),
|
||||
url="url",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def withdrawal_state_failed():
|
||||
return RevenueWithdrawalStateFailed()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def withdrawal_state_pending():
|
||||
return RevenueWithdrawalStatePending()
|
||||
|
||||
|
||||
def transaction_partner_user():
|
||||
return TransactionPartnerUser(
|
||||
user=User(id=1, is_bot=False, first_name="first_name", username="username"),
|
||||
affiliate=AffiliateInfo(
|
||||
affiliate_user=User(id=2, is_bot=True, first_name="first_name", username="username"),
|
||||
affiliate_chat=Chat(id=3, type="private", title="title"),
|
||||
commission_per_mille=1,
|
||||
amount=2,
|
||||
nanostar_amount=3,
|
||||
),
|
||||
invoice_payload="payload",
|
||||
paid_media=[
|
||||
PaidMediaPhoto(
|
||||
photo=[
|
||||
PhotoSize(
|
||||
file_id="file_id", width=1, height=1, file_unique_id="file_unique_id"
|
||||
)
|
||||
]
|
||||
)
|
||||
],
|
||||
paid_media_payload="payload",
|
||||
subscription_period=datetime.timedelta(days=1),
|
||||
gift=Gift(
|
||||
id="some_id",
|
||||
sticker=Sticker(
|
||||
file_id="file_id",
|
||||
file_unique_id="file_unique_id",
|
||||
width=512,
|
||||
height=512,
|
||||
is_animated=False,
|
||||
is_video=False,
|
||||
type="regular",
|
||||
),
|
||||
star_count=5,
|
||||
total_count=10,
|
||||
remaining_count=5,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def transaction_partner_affiliate_program():
|
||||
return TransactionPartnerAffiliateProgram(
|
||||
sponsor_user=User(id=1, is_bot=True, first_name="first_name", username="username"),
|
||||
commission_per_mille=42,
|
||||
)
|
||||
|
||||
|
||||
def transaction_partner_fragment():
|
||||
return TransactionPartnerFragment(
|
||||
withdrawal_state=withdrawal_state_succeeded(),
|
||||
)
|
||||
|
||||
|
||||
def star_transaction():
|
||||
return StarTransaction(
|
||||
id="1",
|
||||
amount=1,
|
||||
nanostar_amount=365,
|
||||
date=to_timestamp(datetime.datetime(2024, 1, 1, 0, 0, 0, 0, tzinfo=UTC)),
|
||||
source=transaction_partner_user(),
|
||||
receiver=transaction_partner_fragment(),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def star_transactions():
|
||||
return StarTransactions(
|
||||
transactions=[
|
||||
star_transaction(),
|
||||
star_transaction(),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
TransactionPartner.AFFILIATE_PROGRAM,
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
TransactionPartner.TELEGRAM_API,
|
||||
TransactionPartner.USER,
|
||||
],
|
||||
)
|
||||
def tp_scope_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
TransactionPartnerTelegramApi,
|
||||
TransactionPartnerUser,
|
||||
],
|
||||
ids=[
|
||||
TransactionPartner.AFFILIATE_PROGRAM,
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
TransactionPartner.TELEGRAM_API,
|
||||
TransactionPartner.USER,
|
||||
],
|
||||
)
|
||||
def tp_scope_class(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
(TransactionPartnerAffiliateProgram, TransactionPartner.AFFILIATE_PROGRAM),
|
||||
(TransactionPartnerFragment, TransactionPartner.FRAGMENT),
|
||||
(TransactionPartnerOther, TransactionPartner.OTHER),
|
||||
(TransactionPartnerTelegramAds, TransactionPartner.TELEGRAM_ADS),
|
||||
(TransactionPartnerTelegramApi, TransactionPartner.TELEGRAM_API),
|
||||
(TransactionPartnerUser, TransactionPartner.USER),
|
||||
],
|
||||
ids=[
|
||||
TransactionPartner.AFFILIATE_PROGRAM,
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
TransactionPartner.TELEGRAM_API,
|
||||
TransactionPartner.USER,
|
||||
],
|
||||
)
|
||||
def tp_scope_class_and_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def transaction_partner(tp_scope_class_and_type):
|
||||
# We use de_json here so that we don't have to worry about which class gets which arguments
|
||||
return tp_scope_class_and_type[0].de_json(
|
||||
{
|
||||
"type": tp_scope_class_and_type[1],
|
||||
"invoice_payload": TransactionPartnerTestBase.invoice_payload,
|
||||
"withdrawal_state": TransactionPartnerTestBase.withdrawal_state.to_dict(),
|
||||
"user": TransactionPartnerTestBase.user.to_dict(),
|
||||
"affiliate": TransactionPartnerTestBase.affiliate.to_dict(),
|
||||
"request_count": TransactionPartnerTestBase.request_count,
|
||||
"sponsor_user": TransactionPartnerTestBase.sponsor_user.to_dict(),
|
||||
"commission_per_mille": TransactionPartnerTestBase.commission_per_mille,
|
||||
"gift": TransactionPartnerTestBase.gift.to_dict(),
|
||||
"paid_media": [m.to_dict() for m in TransactionPartnerTestBase.paid_media],
|
||||
"paid_media_payload": TransactionPartnerTestBase.paid_media_payload,
|
||||
"subscription_period": TransactionPartnerTestBase.subscription_period.total_seconds(),
|
||||
},
|
||||
bot=None,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
RevenueWithdrawalState.FAILED,
|
||||
RevenueWithdrawalState.SUCCEEDED,
|
||||
RevenueWithdrawalState.PENDING,
|
||||
],
|
||||
)
|
||||
def rws_scope_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
RevenueWithdrawalStateFailed,
|
||||
RevenueWithdrawalStateSucceeded,
|
||||
RevenueWithdrawalStatePending,
|
||||
],
|
||||
ids=[
|
||||
RevenueWithdrawalState.FAILED,
|
||||
RevenueWithdrawalState.SUCCEEDED,
|
||||
RevenueWithdrawalState.PENDING,
|
||||
],
|
||||
)
|
||||
def rws_scope_class(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
(RevenueWithdrawalStateFailed, RevenueWithdrawalState.FAILED),
|
||||
(RevenueWithdrawalStateSucceeded, RevenueWithdrawalState.SUCCEEDED),
|
||||
(RevenueWithdrawalStatePending, RevenueWithdrawalState.PENDING),
|
||||
],
|
||||
ids=[
|
||||
RevenueWithdrawalState.FAILED,
|
||||
RevenueWithdrawalState.SUCCEEDED,
|
||||
RevenueWithdrawalState.PENDING,
|
||||
],
|
||||
)
|
||||
def rws_scope_class_and_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def revenue_withdrawal_state(rws_scope_class_and_type):
|
||||
# We use de_json here so that we don't have to worry about which class gets which arguments
|
||||
return rws_scope_class_and_type[0].de_json(
|
||||
{
|
||||
"type": rws_scope_class_and_type[1],
|
||||
"date": to_timestamp(RevenueWithdrawalStateTestBase.date),
|
||||
"url": RevenueWithdrawalStateTestBase.url,
|
||||
},
|
||||
bot=None,
|
||||
)
|
||||
|
||||
|
||||
class StarTransactionTestBase:
|
||||
id = "2"
|
||||
amount = 2
|
||||
nanostar_amount = 365
|
||||
date = to_timestamp(datetime.datetime(2024, 1, 1, 0, 0, 0, 0, tzinfo=UTC))
|
||||
source = TransactionPartnerUser(
|
||||
user=User(
|
||||
id=2,
|
||||
is_bot=False,
|
||||
first_name="first_name",
|
||||
),
|
||||
)
|
||||
receiver = TransactionPartnerOther()
|
||||
|
||||
|
||||
class TestStarTransactionWithoutRequest(StarTransactionTestBase):
|
||||
def test_slot_behaviour(self):
|
||||
inst = star_transaction()
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"id": self.id,
|
||||
"amount": self.amount,
|
||||
"nanostar_amount": self.nanostar_amount,
|
||||
"date": self.date,
|
||||
"source": self.source.to_dict(),
|
||||
"receiver": self.receiver.to_dict(),
|
||||
}
|
||||
st = StarTransaction.de_json(json_dict, offline_bot)
|
||||
st_none = StarTransaction.de_json(None, offline_bot)
|
||||
assert st.api_kwargs == {}
|
||||
assert st.id == self.id
|
||||
assert st.amount == self.amount
|
||||
assert st.nanostar_amount == self.nanostar_amount
|
||||
assert st.date == from_timestamp(self.date)
|
||||
assert st.source == self.source
|
||||
assert st.receiver == self.receiver
|
||||
assert st_none is None
|
||||
|
||||
def test_de_json_star_transaction_localization(self, tz_bot, offline_bot, raw_bot):
|
||||
json_dict = star_transaction().to_dict()
|
||||
st_raw = StarTransaction.de_json(json_dict, raw_bot)
|
||||
st_bot = StarTransaction.de_json(json_dict, offline_bot)
|
||||
st_tz = StarTransaction.de_json(json_dict, tz_bot)
|
||||
|
||||
# comparing utcoffsets because comparing timezones is unpredicatable
|
||||
st_offset = st_tz.date.utcoffset()
|
||||
tz_bot_offset = tz_bot.defaults.tzinfo.utcoffset(st_tz.date.replace(tzinfo=None))
|
||||
|
||||
assert st_raw.date.tzinfo == UTC
|
||||
assert st_bot.date.tzinfo == UTC
|
||||
assert st_offset == tz_bot_offset
|
||||
|
||||
def test_to_dict(self):
|
||||
st = star_transaction()
|
||||
expected_dict = {
|
||||
"id": "1",
|
||||
"amount": 1,
|
||||
"nanostar_amount": 365,
|
||||
"date": st.date,
|
||||
"source": st.source.to_dict(),
|
||||
"receiver": st.receiver.to_dict(),
|
||||
}
|
||||
assert st.to_dict() == expected_dict
|
||||
|
||||
def test_equality(self):
|
||||
a = StarTransaction(
|
||||
id=self.id,
|
||||
amount=self.amount,
|
||||
date=self.date,
|
||||
source=self.source,
|
||||
receiver=self.receiver,
|
||||
)
|
||||
b = StarTransaction(
|
||||
id=self.id,
|
||||
amount=self.amount,
|
||||
date=None,
|
||||
source=self.source,
|
||||
receiver=self.receiver,
|
||||
)
|
||||
c = StarTransaction(
|
||||
id="3",
|
||||
amount=3,
|
||||
date=to_timestamp(datetime.datetime.utcnow()),
|
||||
source=TransactionPartnerUser(
|
||||
user=User(
|
||||
id=3,
|
||||
is_bot=False,
|
||||
first_name="first_name",
|
||||
),
|
||||
),
|
||||
receiver=TransactionPartnerOther(),
|
||||
)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
|
||||
class StarTransactionsTestBase:
|
||||
transactions = [star_transaction(), star_transaction()]
|
||||
|
||||
|
||||
class TestStarTransactionsWithoutRequest(StarTransactionsTestBase):
|
||||
def test_slot_behaviour(self, star_transactions):
|
||||
inst = star_transactions
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"transactions": [t.to_dict() for t in self.transactions],
|
||||
}
|
||||
st = StarTransactions.de_json(json_dict, offline_bot)
|
||||
st_none = StarTransactions.de_json(None, offline_bot)
|
||||
assert st.api_kwargs == {}
|
||||
assert st.transactions == tuple(self.transactions)
|
||||
assert st_none is None
|
||||
|
||||
def test_to_dict(self, star_transactions):
|
||||
expected_dict = {
|
||||
"transactions": [t.to_dict() for t in self.transactions],
|
||||
}
|
||||
assert star_transactions.to_dict() == expected_dict
|
||||
|
||||
def test_equality(self):
|
||||
a = StarTransactions(
|
||||
transactions=self.transactions,
|
||||
)
|
||||
b = StarTransactions(
|
||||
transactions=self.transactions,
|
||||
)
|
||||
c = StarTransactions(
|
||||
transactions=[star_transaction()],
|
||||
)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
|
||||
class TransactionPartnerTestBase:
|
||||
withdrawal_state = withdrawal_state_succeeded()
|
||||
user = transaction_partner_user().user
|
||||
affiliate = transaction_partner_user().affiliate
|
||||
invoice_payload = "payload"
|
||||
request_count = 42
|
||||
sponsor_user = transaction_partner_affiliate_program().sponsor_user
|
||||
commission_per_mille = transaction_partner_affiliate_program().commission_per_mille
|
||||
gift = transaction_partner_user().gift
|
||||
paid_media = transaction_partner_user().paid_media
|
||||
paid_media_payload = transaction_partner_user().paid_media_payload
|
||||
subscription_period = transaction_partner_user().subscription_period
|
||||
|
||||
|
||||
class TestTransactionPartnerWithoutRequest(TransactionPartnerTestBase):
|
||||
def test_slot_behaviour(self, transaction_partner):
|
||||
inst = transaction_partner
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot, tp_scope_class_and_type):
|
||||
cls = tp_scope_class_and_type[0]
|
||||
type_ = tp_scope_class_and_type[1]
|
||||
|
||||
json_dict = {
|
||||
"type": type_,
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
"affiliate": self.affiliate.to_dict(),
|
||||
"request_count": self.request_count,
|
||||
"sponsor_user": self.sponsor_user.to_dict(),
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
}
|
||||
tp = TransactionPartner.de_json(json_dict, offline_bot)
|
||||
assert set(tp.api_kwargs.keys()) == {
|
||||
"user",
|
||||
"affiliate",
|
||||
"withdrawal_state",
|
||||
"invoice_payload",
|
||||
"request_count",
|
||||
"sponsor_user",
|
||||
"commission_per_mille",
|
||||
} - set(cls.__slots__)
|
||||
|
||||
assert isinstance(tp, TransactionPartner)
|
||||
assert type(tp) is cls
|
||||
assert tp.type == type_
|
||||
for key in json_dict:
|
||||
if key in cls.__slots__:
|
||||
assert getattr(tp, key) == getattr(self, key)
|
||||
|
||||
assert cls.de_json(None, offline_bot) is None
|
||||
assert TransactionPartner.de_json({}, offline_bot) is None
|
||||
|
||||
def test_de_json_invalid_type(self, offline_bot):
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
"affiliate": self.affiliate.to_dict(),
|
||||
"request_count": self.request_count,
|
||||
"sponsor_user": self.sponsor_user.to_dict(),
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
}
|
||||
tp = TransactionPartner.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
"affiliate": self.affiliate.to_dict(),
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"request_count": self.request_count,
|
||||
"sponsor_user": self.sponsor_user.to_dict(),
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
}
|
||||
|
||||
assert type(tp) is TransactionPartner
|
||||
assert tp.type == "invalid"
|
||||
|
||||
def test_de_json_subclass(self, tp_scope_class, offline_bot):
|
||||
"""This makes sure that e.g. TransactionPartnerUser(data) never returns a
|
||||
TransactionPartnerFragment instance."""
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
"affiliate": self.affiliate.to_dict(),
|
||||
"request_count": self.request_count,
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
}
|
||||
assert type(tp_scope_class.de_json(json_dict, offline_bot)) is tp_scope_class
|
||||
|
||||
def test_to_dict(self, transaction_partner):
|
||||
tp_dict = transaction_partner.to_dict()
|
||||
|
||||
assert isinstance(tp_dict, dict)
|
||||
assert tp_dict["type"] == transaction_partner.type
|
||||
for attr in transaction_partner.__slots__:
|
||||
attribute = getattr(transaction_partner, attr)
|
||||
if isinstance(attribute, TelegramObject):
|
||||
assert tp_dict[attr] == attribute.to_dict()
|
||||
elif not isinstance(attribute, str) and isinstance(attribute, Sequence):
|
||||
assert tp_dict[attr] == [a.to_dict() for a in attribute]
|
||||
elif isinstance(attribute, datetime.timedelta):
|
||||
assert tp_dict[attr] == attribute.total_seconds()
|
||||
else:
|
||||
assert tp_dict[attr] == attribute
|
||||
|
||||
def test_type_enum_conversion(self):
|
||||
assert type(TransactionPartner("other").type) is TransactionPartnerType
|
||||
assert TransactionPartner("unknown").type == "unknown"
|
||||
|
||||
def test_equality(self, transaction_partner, offline_bot):
|
||||
a = TransactionPartner("base_type")
|
||||
b = TransactionPartner("base_type")
|
||||
c = transaction_partner
|
||||
d = deepcopy(transaction_partner)
|
||||
e = Dice(4, "emoji")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
assert c == d
|
||||
assert hash(c) == hash(d)
|
||||
|
||||
assert c != e
|
||||
assert hash(c) != hash(e)
|
||||
|
||||
if hasattr(c, "user"):
|
||||
json_dict = c.to_dict()
|
||||
json_dict["user"] = User(2, "something", True).to_dict()
|
||||
f = c.__class__.de_json(json_dict, offline_bot)
|
||||
|
||||
assert c != f
|
||||
assert hash(c) != hash(f)
|
||||
|
||||
if hasattr(c, "request_count"):
|
||||
json_dict = c.to_dict()
|
||||
json_dict["request_count"] = 1
|
||||
f = c.__class__.de_json(json_dict, offline_bot)
|
||||
|
||||
assert c != f
|
||||
assert hash(c) != hash(f)
|
||||
|
||||
|
||||
class TestTransactionPartnerUserWithoutRequest(TransactionPartnerTestBase):
|
||||
def test_de_json_required(self, offline_bot):
|
||||
json_dict = {
|
||||
"user": transaction_partner_user().user.to_dict(),
|
||||
}
|
||||
tp = TransactionPartnerUser.de_json(json_dict, offline_bot)
|
||||
assert tp.api_kwargs == {}
|
||||
assert tp.user == transaction_partner_user().user
|
||||
|
||||
# This test is here mainly to check that the below cases work
|
||||
assert tp.subscription_period is None
|
||||
assert tp.gift is None
|
||||
|
||||
|
||||
class RevenueWithdrawalStateTestBase:
|
||||
date = datetime.datetime(2024, 1, 1, 0, 0, 0, 0, tzinfo=UTC)
|
||||
url = "url"
|
||||
|
||||
|
||||
class TestRevenueWithdrawalStateWithoutRequest(RevenueWithdrawalStateTestBase):
|
||||
def test_slot_behaviour(self, revenue_withdrawal_state):
|
||||
inst = revenue_withdrawal_state
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot, rws_scope_class_and_type):
|
||||
cls = rws_scope_class_and_type[0]
|
||||
type_ = rws_scope_class_and_type[1]
|
||||
|
||||
json_dict = {
|
||||
"type": type_,
|
||||
"date": to_timestamp(self.date),
|
||||
"url": self.url,
|
||||
}
|
||||
rws = RevenueWithdrawalState.de_json(json_dict, offline_bot)
|
||||
assert set(rws.api_kwargs.keys()) == {"date", "url"} - set(cls.__slots__)
|
||||
|
||||
assert isinstance(rws, RevenueWithdrawalState)
|
||||
assert type(rws) is cls
|
||||
assert rws.type == type_
|
||||
if "date" in cls.__slots__:
|
||||
assert rws.date == self.date
|
||||
if "url" in cls.__slots__:
|
||||
assert rws.url == self.url
|
||||
|
||||
assert cls.de_json(None, offline_bot) is None
|
||||
assert RevenueWithdrawalState.de_json({}, offline_bot) is None
|
||||
|
||||
def test_de_json_invalid_type(self, offline_bot):
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"date": to_timestamp(self.date),
|
||||
"url": self.url,
|
||||
}
|
||||
rws = RevenueWithdrawalState.de_json(json_dict, offline_bot)
|
||||
assert rws.api_kwargs == {
|
||||
"date": to_timestamp(self.date),
|
||||
"url": self.url,
|
||||
}
|
||||
|
||||
assert type(rws) is RevenueWithdrawalState
|
||||
assert rws.type == "invalid"
|
||||
|
||||
def test_de_json_subclass(self, rws_scope_class, offline_bot):
|
||||
"""This makes sure that e.g. RevenueWithdrawalState(data) never returns a
|
||||
RevenueWithdrawalStateFailed instance."""
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"date": to_timestamp(self.date),
|
||||
"url": self.url,
|
||||
}
|
||||
assert type(rws_scope_class.de_json(json_dict, offline_bot)) is rws_scope_class
|
||||
|
||||
def test_to_dict(self, revenue_withdrawal_state):
|
||||
rws_dict = revenue_withdrawal_state.to_dict()
|
||||
|
||||
assert isinstance(rws_dict, dict)
|
||||
assert rws_dict["type"] == revenue_withdrawal_state.type
|
||||
if hasattr(revenue_withdrawal_state, "date"):
|
||||
assert rws_dict["date"] == to_timestamp(revenue_withdrawal_state.date)
|
||||
if hasattr(revenue_withdrawal_state, "url"):
|
||||
assert rws_dict["url"] == revenue_withdrawal_state.url
|
||||
|
||||
def test_type_enum_conversion(self):
|
||||
assert type(RevenueWithdrawalState("failed").type) is RevenueWithdrawalStateType
|
||||
assert RevenueWithdrawalState("unknown").type == "unknown"
|
||||
|
||||
def test_equality(self, revenue_withdrawal_state, offline_bot):
|
||||
a = RevenueWithdrawalState("base_type")
|
||||
b = RevenueWithdrawalState("base_type")
|
||||
c = revenue_withdrawal_state
|
||||
d = deepcopy(revenue_withdrawal_state)
|
||||
e = Dice(4, "emoji")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
assert c == d
|
||||
assert hash(c) == hash(d)
|
||||
|
||||
assert c != e
|
||||
assert hash(c) != hash(e)
|
||||
|
||||
if hasattr(c, "url"):
|
||||
json_dict = c.to_dict()
|
||||
json_dict["url"] = "something"
|
||||
f = c.__class__.de_json(json_dict, offline_bot)
|
||||
|
||||
assert c == f
|
||||
assert hash(c) == hash(f)
|
||||
|
||||
if hasattr(c, "date"):
|
||||
json_dict = c.to_dict()
|
||||
json_dict["date"] = to_timestamp(datetime.datetime.utcnow())
|
||||
f = c.__class__.de_json(json_dict, offline_bot)
|
||||
|
||||
assert c != f
|
||||
assert hash(c) != hash(f)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def affiliate_info():
|
||||
return AffiliateInfo(
|
||||
affiliate_user=AffiliateInfoTestBase.affiliate_user,
|
||||
affiliate_chat=AffiliateInfoTestBase.affiliate_chat,
|
||||
commission_per_mille=AffiliateInfoTestBase.commission_per_mille,
|
||||
amount=AffiliateInfoTestBase.amount,
|
||||
nanostar_amount=AffiliateInfoTestBase.nanostar_amount,
|
||||
)
|
||||
|
||||
|
||||
class AffiliateInfoTestBase:
|
||||
affiliate_user = User(id=1, is_bot=True, first_name="affiliate_user", username="username")
|
||||
affiliate_chat = Chat(id=2, type="private", title="affiliate_chat")
|
||||
commission_per_mille = 13
|
||||
amount = 14
|
||||
nanostar_amount = -42
|
||||
|
||||
|
||||
class TestAffiliateInfoWithoutRequest(AffiliateInfoTestBase):
|
||||
def test_slot_behaviour(self, affiliate_info):
|
||||
inst = affiliate_info
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, offline_bot):
|
||||
json_dict = {
|
||||
"affiliate_user": self.affiliate_user.to_dict(),
|
||||
"affiliate_chat": self.affiliate_chat.to_dict(),
|
||||
"commission_per_mille": self.commission_per_mille,
|
||||
"amount": self.amount,
|
||||
"nanostar_amount": self.nanostar_amount,
|
||||
}
|
||||
ai = AffiliateInfo.de_json(json_dict, offline_bot)
|
||||
assert ai.api_kwargs == {}
|
||||
assert ai.affiliate_user == self.affiliate_user
|
||||
assert ai.affiliate_chat == self.affiliate_chat
|
||||
assert ai.commission_per_mille == self.commission_per_mille
|
||||
assert ai.amount == self.amount
|
||||
assert ai.nanostar_amount == self.nanostar_amount
|
||||
|
||||
assert AffiliateInfo.de_json(None, offline_bot) is None
|
||||
assert AffiliateInfo.de_json({}, offline_bot) is None
|
||||
|
||||
def test_to_dict(self, affiliate_info):
|
||||
ai_dict = affiliate_info.to_dict()
|
||||
|
||||
assert isinstance(ai_dict, dict)
|
||||
assert ai_dict["affiliate_user"] == affiliate_info.affiliate_user.to_dict()
|
||||
assert ai_dict["affiliate_chat"] == affiliate_info.affiliate_chat.to_dict()
|
||||
assert ai_dict["commission_per_mille"] == affiliate_info.commission_per_mille
|
||||
assert ai_dict["amount"] == affiliate_info.amount
|
||||
assert ai_dict["nanostar_amount"] == affiliate_info.nanostar_amount
|
||||
|
||||
def test_equality(self, affiliate_info, offline_bot):
|
||||
a = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
b = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
c = AffiliateInfo(
|
||||
affiliate_user=User(id=3, is_bot=True, first_name="first_name", username="username"),
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
d = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=Chat(id=3, type="private", title="title"),
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
e = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=1,
|
||||
amount=self.amount,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
f = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=1,
|
||||
nanostar_amount=self.nanostar_amount,
|
||||
)
|
||||
g = AffiliateInfo(
|
||||
affiliate_user=self.affiliate_user,
|
||||
affiliate_chat=self.affiliate_chat,
|
||||
commission_per_mille=self.commission_per_mille,
|
||||
amount=self.amount,
|
||||
nanostar_amount=1,
|
||||
)
|
||||
h = Dice(4, "emoji")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
assert a != f
|
||||
assert hash(a) != hash(f)
|
||||
|
||||
assert a != g
|
||||
assert hash(a) != hash(g)
|
||||
|
||||
assert a != h
|
||||
assert hash(a) != hash(h)
|
|
@ -16,7 +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 datetime
|
||||
import datetime as dtm
|
||||
import inspect
|
||||
import pickle
|
||||
import re
|
||||
|
@ -176,9 +176,7 @@ class TestTelegramObject:
|
|||
assert to.to_dict() == {"foo": "bar"}
|
||||
|
||||
def test_to_dict_missing_attribute(self):
|
||||
message = Message(
|
||||
1, datetime.datetime.now(), Chat(1, "private"), from_user=User(1, "", False)
|
||||
)
|
||||
message = Message(1, dtm.datetime.now(), Chat(1, "private"), from_user=User(1, "", False))
|
||||
message._unfreeze()
|
||||
del message.chat
|
||||
|
||||
|
@ -288,7 +286,7 @@ class TestTelegramObject:
|
|||
def test_pickle(self, bot):
|
||||
chat = Chat(2, Chat.PRIVATE)
|
||||
user = User(3, "first_name", False)
|
||||
date = datetime.datetime.now()
|
||||
date = dtm.datetime.now()
|
||||
photo = PhotoSize("file_id", "unique", 21, 21)
|
||||
photo.set_bot(bot)
|
||||
msg = Message(
|
||||
|
@ -432,7 +430,7 @@ class PicklePropertyTest(TelegramObject):
|
|||
def test_deepcopy_telegram_obj(self, bot):
|
||||
chat = Chat(2, Chat.PRIVATE)
|
||||
user = User(3, "first_name", False)
|
||||
date = datetime.datetime.now()
|
||||
date = dtm.datetime.now()
|
||||
photo = PhotoSize("file_id", "unique", 21, 21)
|
||||
photo.set_bot(bot)
|
||||
msg = Message(
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
#
|
||||
# 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 time
|
||||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -57,7 +57,7 @@ from tests.auxil.slots import mro_slots
|
|||
|
||||
message = Message(
|
||||
1,
|
||||
datetime.utcnow(),
|
||||
dtm.datetime.utcnow(),
|
||||
Chat(1, ""),
|
||||
from_user=User(1, "", False),
|
||||
text="Text",
|
||||
|
@ -65,7 +65,7 @@ message = Message(
|
|||
)
|
||||
channel_post = Message(
|
||||
1,
|
||||
datetime.utcnow(),
|
||||
dtm.datetime.utcnow(),
|
||||
Chat(1, ""),
|
||||
text="Text",
|
||||
sender_chat=Chat(1, ""),
|
||||
|
@ -139,7 +139,7 @@ deleted_business_messages = BusinessMessagesDeleted(
|
|||
|
||||
business_message = Message(
|
||||
1,
|
||||
datetime.utcnow(),
|
||||
dtm.datetime.utcnow(),
|
||||
Chat(1, ""),
|
||||
User(1, "", False),
|
||||
)
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import datetime as dtm
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -89,12 +89,12 @@ class TestWebhookInfoWithoutRequest(WebhookInfoTestBase):
|
|||
assert webhook_info.url == self.url
|
||||
assert webhook_info.has_custom_certificate == self.has_custom_certificate
|
||||
assert webhook_info.pending_update_count == self.pending_update_count
|
||||
assert isinstance(webhook_info.last_error_date, datetime)
|
||||
assert isinstance(webhook_info.last_error_date, dtm.datetime)
|
||||
assert webhook_info.last_error_date == from_timestamp(self.last_error_date)
|
||||
assert webhook_info.max_connections == self.max_connections
|
||||
assert webhook_info.allowed_updates == tuple(self.allowed_updates)
|
||||
assert webhook_info.ip_address == self.ip_address
|
||||
assert isinstance(webhook_info.last_synchronization_error_date, datetime)
|
||||
assert isinstance(webhook_info.last_synchronization_error_date, dtm.datetime)
|
||||
assert webhook_info.last_synchronization_error_date == from_timestamp(
|
||||
self.last_synchronization_error_date
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue