mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-19 15:45:13 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ac52018c2 | |||
| ca7a30963d | |||
| c42a230b96 | |||
| ce9742a602 | |||
| 43279543a3 | |||
| eda2172617 | |||
| 89dfa37dbf | |||
| 3709c2fa93 | |||
| da93fe94ae |
@@ -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.11.1
|
||||
- uses: srvaroa/labeler@v1.12.0
|
||||
# Config file at .github/labeler.yml
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
.test_report_optionals_junit.xml
|
||||
|
||||
- name: Submit coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
env_vars: OS,PYTHON
|
||||
name: ${{ matrix.os }}-${{ matrix.python-version }}
|
||||
|
||||
@@ -16,7 +16,7 @@ repos:
|
||||
- tornado~=6.4
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools>=5.3.3,<5.5.0
|
||||
- aiolimiter~=1.1.0
|
||||
- aiolimiter~=1.1,<1.3
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.4.2
|
||||
hooks:
|
||||
@@ -29,7 +29,7 @@ repos:
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: v3.2.4
|
||||
rev: v3.3.2
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^(?!(tests|docs)).*\.py$
|
||||
@@ -38,7 +38,7 @@ repos:
|
||||
- tornado~=6.4
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools>=5.3.3,<5.5.0
|
||||
- aiolimiter~=1.1.0
|
||||
- aiolimiter~=1.1,<1.3
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.10.1
|
||||
@@ -54,7 +54,7 @@ repos:
|
||||
- tornado~=6.4
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools>=5.3.3,<5.5.0
|
||||
- aiolimiter~=1.1.0
|
||||
- aiolimiter~=1.1,<1.3
|
||||
- . # this basically does `pip install -e .`
|
||||
- id: mypy
|
||||
name: mypy-examples
|
||||
|
||||
@@ -81,6 +81,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `LRezende <https://github.com/lrezende>`_
|
||||
- `Luca Bellanti <https://github.com/Trifase>`_
|
||||
- `Lucas Molinari <https://github.com/lucasmolinari>`_
|
||||
- `Luis Pérez <https://github.com/nemacysts>`_
|
||||
- `macrojames <https://github.com/macrojames>`_
|
||||
- `Matheus Lemos <https://github.com/mlemosf>`_
|
||||
- `Michael Dix <https://github.com/Eisberge>`_
|
||||
|
||||
+27
@@ -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*
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-8.0-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-8.1-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API version
|
||||
|
||||
@@ -81,7 +81,7 @@ After installing_ the library, be sure to check out the section on `working with
|
||||
Telegram API support
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
All types and methods of the Telegram Bot API **8.0** are natively supported by this library.
|
||||
All types and methods of the Telegram Bot API **8.1** are natively supported by this library.
|
||||
In addition, Bot API functionality not yet natively included can still be used as described `in our wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Forward-Compatibility>`_.
|
||||
|
||||
Notable Features
|
||||
@@ -155,7 +155,7 @@ PTB can be installed with optional dependencies:
|
||||
* ``pip install "python-telegram-bot[passport]"`` installs the `cryptography>=39.0.1 <https://cryptography.io/en/stable>`_ library. Use this, if you want to use Telegram Passport related functionality.
|
||||
* ``pip install "python-telegram-bot[socks]"`` installs `httpx[socks] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to work behind a Socks5 server.
|
||||
* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
|
||||
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
|
||||
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1,<1.3 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
|
||||
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.4 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
|
||||
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools>=5.3.3,<5.6.0 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
|
||||
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library and enforces `pytz>=2018.6 <https://pypi.org/project/pytz/>`_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``.
|
||||
|
||||
@@ -187,6 +187,11 @@ def autodoc_process_bases(app, name, obj, option, bases: list) -> None:
|
||||
bases[idx] = ":class:`enum.IntEnum`"
|
||||
continue
|
||||
|
||||
if "FloatEnum" in base:
|
||||
bases[idx] = ":class:`enum.Enum`"
|
||||
bases.insert(0, ":class:`float`")
|
||||
continue
|
||||
|
||||
# Drop generics (at least for now)
|
||||
if base.endswith("]"):
|
||||
base = base.split("[", maxsplit=1)[0]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
AffiliateInfo
|
||||
=============
|
||||
|
||||
.. autoclass:: telegram.AffiliateInfo
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -5,5 +5,5 @@ telegram.constants Module
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:no-undoc-members:
|
||||
:inherited-members: Enum, EnumMeta, str, int
|
||||
:inherited-members: Enum, EnumMeta, str, int, float
|
||||
:exclude-members: __format__, __new__, __repr__, __str__
|
||||
|
||||
@@ -9,6 +9,7 @@ Your bot can accept payments from Telegram users. Please see the `introduction t
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
telegram.affiliateinfo
|
||||
telegram.invoice
|
||||
telegram.labeledprice
|
||||
telegram.orderinfo
|
||||
@@ -25,6 +26,7 @@ Your bot can accept payments from Telegram users. Please see the `introduction t
|
||||
telegram.startransactions
|
||||
telegram.successfulpayment
|
||||
telegram.transactionpartner
|
||||
telegram.transactionpartneraffiliateprogram
|
||||
telegram.transactionpartnerfragment
|
||||
telegram.transactionpartnerother
|
||||
telegram.transactionpartnertelegramads
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
TransactionPartnerAffiliateProgram
|
||||
===================================
|
||||
|
||||
.. autoclass:: telegram.TransactionPartnerAffiliateProgram
|
||||
:members:
|
||||
:show-inheritance:
|
||||
+3
-2
@@ -86,7 +86,7 @@ passport = [
|
||||
"cffi >= 1.17.0rc1; python_version > '3.12'"
|
||||
]
|
||||
rate-limiter = [
|
||||
"aiolimiter~=1.1.0",
|
||||
"aiolimiter>=1.1,<1.3",
|
||||
]
|
||||
socks = [
|
||||
"httpx[socks]",
|
||||
@@ -149,7 +149,8 @@ enable = ["useless-suppression"]
|
||||
disable = ["duplicate-code", "too-many-arguments", "too-many-public-methods",
|
||||
"too-few-public-methods", "broad-exception-caught", "too-many-instance-attributes",
|
||||
"fixme", "missing-function-docstring", "missing-class-docstring", "too-many-locals",
|
||||
"too-many-lines", "too-many-branches", "too-many-statements", "cyclic-import"
|
||||
"too-many-lines", "too-many-branches", "too-many-statements", "cyclic-import",
|
||||
"too-many-positional-arguments",
|
||||
]
|
||||
|
||||
[tool.pylint.main]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
build
|
||||
|
||||
# For the test suite
|
||||
pytest==8.3.3
|
||||
pytest==8.3.4
|
||||
|
||||
# needed because pytest doesn't come with native support for coroutines as tests
|
||||
pytest-asyncio==0.21.2
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
__author__ = "devs@python-telegram-bot.org"
|
||||
__all__ = (
|
||||
"AffiliateInfo",
|
||||
"Animation",
|
||||
"Audio",
|
||||
"BackgroundFill",
|
||||
@@ -236,6 +237,7 @@ __all__ = (
|
||||
"TelegramObject",
|
||||
"TextQuote",
|
||||
"TransactionPartner",
|
||||
"TransactionPartnerAffiliateProgram",
|
||||
"TransactionPartnerFragment",
|
||||
"TransactionPartnerOther",
|
||||
"TransactionPartnerTelegramAds",
|
||||
@@ -469,6 +471,7 @@ from ._payment.shippingaddress import ShippingAddress
|
||||
from ._payment.shippingoption import ShippingOption
|
||||
from ._payment.shippingquery import ShippingQuery
|
||||
from ._payment.stars import (
|
||||
AffiliateInfo,
|
||||
RevenueWithdrawalState,
|
||||
RevenueWithdrawalStateFailed,
|
||||
RevenueWithdrawalStatePending,
|
||||
@@ -476,6 +479,7 @@ from ._payment.stars import (
|
||||
StarTransaction,
|
||||
StarTransactions,
|
||||
TransactionPartner,
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
|
||||
+4
-1
@@ -8175,7 +8175,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
``“XTR”`` (Telegram Stars) if the parameter is used. Currently, it must always be
|
||||
:tg-const:`telegram.constants.InvoiceLimit.SUBSCRIPTION_PERIOD` if specified. Any
|
||||
number of subscriptions can be active for a given bot at the same time, including
|
||||
multiple concurrent subscriptions from the same user.
|
||||
multiple concurrent subscriptions from the same user. Subscription price must
|
||||
not exceed
|
||||
:tg-const:`telegram.constants.InvoiceLimit.SUBSCRIPTION_MAX_PRICE`
|
||||
Telegram Stars.
|
||||
|
||||
.. versionadded:: 21.8
|
||||
max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the
|
||||
|
||||
+188
-3
@@ -24,6 +24,7 @@ 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
|
||||
@@ -194,13 +195,107 @@ class RevenueWithdrawalStateFailed(RevenueWithdrawalState):
|
||||
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:: 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)
|
||||
|
||||
|
||||
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
|
||||
@@ -217,6 +312,11 @@ class TransactionPartner(TelegramObject):
|
||||
|
||||
__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
|
||||
@@ -259,6 +359,7 @@ class TransactionPartner(TelegramObject):
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[TransactionPartner]] = {
|
||||
cls.AFFILIATE_PROGRAM: TransactionPartnerAffiliateProgram,
|
||||
cls.FRAGMENT: TransactionPartnerFragment,
|
||||
cls.USER: TransactionPartnerUser,
|
||||
cls.TELEGRAM_ADS: TransactionPartnerTelegramAds,
|
||||
@@ -272,6 +373,64 @@ class TransactionPartner(TelegramObject):
|
||||
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.
|
||||
|
||||
@@ -328,6 +487,10 @@ class TransactionPartnerUser(TransactionPartner):
|
||||
|
||||
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
|
||||
@@ -348,6 +511,10 @@ class TransactionPartnerUser(TransactionPartner):
|
||||
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
|
||||
@@ -367,6 +534,7 @@ class TransactionPartnerUser(TransactionPartner):
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"affiliate",
|
||||
"gift",
|
||||
"invoice_payload",
|
||||
"paid_media",
|
||||
@@ -383,6 +551,7 @@ class TransactionPartnerUser(TransactionPartner):
|
||||
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:
|
||||
@@ -390,6 +559,7 @@ class TransactionPartnerUser(TransactionPartner):
|
||||
|
||||
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
|
||||
@@ -412,6 +582,7 @@ class TransactionPartnerUser(TransactionPartner):
|
||||
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)
|
||||
@@ -499,7 +670,13 @@ class StarTransaction(TelegramObject):
|
||||
of the original transaction for refund transactions.
|
||||
Coincides with :attr:`SuccessfulPayment.telegram_payment_charge_id` for
|
||||
successful incoming payments from users.
|
||||
amount (:obj:`int`): Number of Telegram Stars transferred by the transaction.
|
||||
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).
|
||||
@@ -513,7 +690,13 @@ class StarTransaction(TelegramObject):
|
||||
of the original transaction for refund transactions.
|
||||
Coincides with :attr:`SuccessfulPayment.telegram_payment_charge_id` for
|
||||
successful incoming payments from users.
|
||||
amount (:obj:`int`): Number of Telegram Stars transferred by the transaction.
|
||||
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).
|
||||
@@ -523,7 +706,7 @@ class StarTransaction(TelegramObject):
|
||||
outgoing transactions.
|
||||
"""
|
||||
|
||||
__slots__ = ("amount", "date", "id", "receiver", "source")
|
||||
__slots__ = ("amount", "date", "id", "nanostar_amount", "receiver", "source")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -532,6 +715,7 @@ class StarTransaction(TelegramObject):
|
||||
date: dtm.datetime,
|
||||
source: Optional[TransactionPartner] = None,
|
||||
receiver: Optional[TransactionPartner] = None,
|
||||
nanostar_amount: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
@@ -541,6 +725,7 @@ class StarTransaction(TelegramObject):
|
||||
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,
|
||||
|
||||
@@ -74,3 +74,17 @@ class IntEnum(_enum.IntEnum): # pylint: disable=invalid-slots
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
class FloatEnum(float, _enum.Enum):
|
||||
"""Helper class for float enums where ``str(member)`` prints the value, but ``repr(member)``
|
||||
gives ``EnumName.MEMBER_NAME``.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__}.{self.name}>"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram WebhookInfo."""
|
||||
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
@@ -60,7 +61,7 @@ class WebhookInfo(TelegramObject):
|
||||
most recent error that happened when trying to deliver an update via webhook.
|
||||
max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS
|
||||
connections to the webhook for update delivery.
|
||||
allowed_updates (Sequence[:obj:`str`], optional): A list of update types the bot is
|
||||
allowed_updates (Sequence[:obj:`str`], optional): A sequence of update types the bot is
|
||||
subscribed to. Defaults to all update types, except
|
||||
:attr:`telegram.Update.chat_member`.
|
||||
|
||||
@@ -90,7 +91,7 @@ class WebhookInfo(TelegramObject):
|
||||
most recent error that happened when trying to deliver an update via webhook.
|
||||
max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS
|
||||
connections to the webhook for update delivery.
|
||||
allowed_updates (tuple[:obj:`str`]): Optional. A list of update types the bot is
|
||||
allowed_updates (tuple[:obj:`str`]): Optional. A tuple of update types the bot is
|
||||
subscribed to. Defaults to all update types, except
|
||||
:attr:`telegram.Update.chat_member`.
|
||||
|
||||
|
||||
+46
-4
@@ -95,6 +95,7 @@ __all__ = [
|
||||
"ReactionType",
|
||||
"ReplyLimit",
|
||||
"RevenueWithdrawalStateType",
|
||||
"StarTransactions",
|
||||
"StarTransactionsLimit",
|
||||
"StickerFormat",
|
||||
"StickerLimit",
|
||||
@@ -112,7 +113,7 @@ from enum import Enum
|
||||
from typing import Final, NamedTuple, Optional
|
||||
|
||||
from telegram._utils.datetime import UTC
|
||||
from telegram._utils.enum import IntEnum, StringEnum
|
||||
from telegram._utils.enum import FloatEnum, IntEnum, StringEnum
|
||||
|
||||
|
||||
class _BotAPIVersion(NamedTuple):
|
||||
@@ -153,7 +154,7 @@ class _AccentColor(NamedTuple):
|
||||
#: :data:`telegram.__bot_api_version_info__`.
|
||||
#:
|
||||
#: .. versionadded:: 20.0
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=8, minor=0)
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=8, minor=1)
|
||||
#: :obj:`str`: Telegram Bot API
|
||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||
#: :data:`telegram.__bot_api_version__`.
|
||||
@@ -1835,7 +1836,6 @@ class MessageLimit(IntEnum):
|
||||
:paramref:`~telegram.Bot.edit_message_text.text` parameter of
|
||||
:meth:`telegram.Bot.edit_message_text`.
|
||||
"""
|
||||
# TODO this constant is not used. helpers.py contains 64 as a number
|
||||
DEEP_LINK_LENGTH = 64
|
||||
""":obj:`int`: Maximum number of characters for a deep link."""
|
||||
# TODO this constant is not used anywhere
|
||||
@@ -2461,8 +2461,25 @@ class RevenueWithdrawalStateType(StringEnum):
|
||||
""":obj:`str`: A withdrawal failed and the transaction was refunded."""
|
||||
|
||||
|
||||
class StarTransactions(FloatEnum):
|
||||
"""This enum contains constants for :class:`telegram.StarTransaction`.
|
||||
The enum members of this enumeration are instances of :class:`float` and can be treated as
|
||||
such.
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
NANOSTAR_VALUE = 1 / 1000000000
|
||||
""":obj:`float`: The value of one nanostar as used in
|
||||
:attr:`telegram.StarTransaction.nanostar_amount`.
|
||||
"""
|
||||
|
||||
|
||||
class StarTransactionsLimit(IntEnum):
|
||||
"""This enum contains limitations for :class:`telegram.Bot.get_star_transactions`.
|
||||
"""This enum contains limitations for :class:`telegram.Bot.get_star_transactions` and
|
||||
:class:`telegram.StarTransaction`.
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
@@ -2478,6 +2495,20 @@ class StarTransactionsLimit(IntEnum):
|
||||
""":obj:`int`: Maximum value allowed for the
|
||||
:paramref:`~telegram.Bot.get_star_transactions.limit` parameter of
|
||||
:meth:`telegram.Bot.get_star_transactions`."""
|
||||
NANOSTAR_MIN_AMOUNT = -999999999
|
||||
""":obj:`int`: Minimum value allowed for :paramref:`~telegram.AffiliateInfo.nanostar_amount`
|
||||
parameter of :class:`telegram.AffiliateInfo`.
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
NANOSTAR_MAX_AMOUNT = 999999999
|
||||
""":obj:`int`: Maximum value allowed for :paramref:`~telegram.StarTransaction.nanostar_amount`
|
||||
parameter of :class:`telegram.StarTransaction` and
|
||||
:paramref:`~telegram.AffiliateInfo.nanostar_amount` parameter of
|
||||
:class:`telegram.AffiliateInfo`.
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
|
||||
|
||||
class StickerFormat(StringEnum):
|
||||
@@ -2622,6 +2653,11 @@ class TransactionPartnerType(StringEnum):
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
AFFILIATE_PROGRAM = "affiliate_program"
|
||||
""":obj:`str`: Transaction with Affiliate Program.
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
FRAGMENT = "fragment"
|
||||
""":obj:`str`: Withdrawal transaction with Fragment."""
|
||||
OTHER = "other"
|
||||
@@ -2925,6 +2961,12 @@ class InvoiceLimit(IntEnum):
|
||||
|
||||
.. versionadded:: 21.8
|
||||
"""
|
||||
SUBSCRIPTION_MAX_PRICE = 2500
|
||||
""":obj:`int`: The maximum price of a subscription created wtih
|
||||
:meth:`telegram.Bot.create_invoice_link`.
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
|
||||
|
||||
class UserProfilePhotosLimit(IntEnum):
|
||||
|
||||
@@ -746,7 +746,7 @@ class Application(
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
allowed_updates: Optional[list[str]] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
drop_pending_updates: Optional[bool] = None,
|
||||
close_loop: bool = True,
|
||||
stop_signals: ODVInput[Sequence[int]] = DEFAULT_NONE,
|
||||
@@ -823,8 +823,11 @@ class Application(
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout`.
|
||||
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
|
||||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
allowed_updates (list[:obj:`str`], optional): Passed to
|
||||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.get_updates`.
|
||||
|
||||
.. 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`.
|
||||
|
||||
@@ -888,7 +891,7 @@ class Application(
|
||||
key: Optional[Union[str, Path]] = None,
|
||||
bootstrap_retries: int = 0,
|
||||
webhook_url: Optional[str] = None,
|
||||
allowed_updates: Optional[list[str]] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
drop_pending_updates: Optional[bool] = None,
|
||||
ip_address: Optional[str] = None,
|
||||
max_connections: int = 40,
|
||||
@@ -954,8 +957,11 @@ class Application(
|
||||
webhook_url (:obj:`str`, optional): Explicitly specify the webhook url. Useful behind
|
||||
NAT, reverse proxy, etc. Default is derived from :paramref:`listen`,
|
||||
:paramref:`port`, :paramref:`url_path`, :paramref:`cert`, and :paramref:`key`.
|
||||
allowed_updates (list[:obj:`str`], optional): Passed to
|
||||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.set_webhook`.
|
||||
|
||||
.. 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`.
|
||||
ip_address (:obj:`str`, optional): Passed to :meth:`telegram.Bot.set_webhook`.
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
# 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 Updater, which tries to make creating Telegram bots intuitive."""
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import ssl
|
||||
from collections.abc import Coroutine
|
||||
from collections.abc import Coroutine, Sequence
|
||||
from pathlib import Path
|
||||
from types import TracebackType
|
||||
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
|
||||
@@ -210,7 +211,7 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
allowed_updates: Optional[list[str]] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
drop_pending_updates: Optional[bool] = None,
|
||||
error_callback: Optional[Callable[[TelegramError], None]] = None,
|
||||
) -> "asyncio.Queue[object]":
|
||||
@@ -265,8 +266,11 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
||||
Deprecated in favor of setting the timeout via
|
||||
:meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout` or
|
||||
:paramref:`telegram.Bot.get_updates_request`.
|
||||
allowed_updates (list[:obj:`str`], optional): Passed to
|
||||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.get_updates`.
|
||||
|
||||
.. 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`.
|
||||
|
||||
@@ -344,7 +348,7 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
||||
pool_timeout: ODVInput[float],
|
||||
bootstrap_retries: int,
|
||||
drop_pending_updates: Optional[bool],
|
||||
allowed_updates: Optional[list[str]],
|
||||
allowed_updates: Optional[Sequence[str]],
|
||||
ready: asyncio.Event,
|
||||
error_callback: Optional[Callable[[TelegramError], None]],
|
||||
) -> None:
|
||||
@@ -457,7 +461,7 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
||||
key: Optional[Union[str, Path]] = None,
|
||||
bootstrap_retries: int = 0,
|
||||
webhook_url: Optional[str] = None,
|
||||
allowed_updates: Optional[list[str]] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
drop_pending_updates: Optional[bool] = None,
|
||||
ip_address: Optional[str] = None,
|
||||
max_connections: int = 40,
|
||||
@@ -516,8 +520,11 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
||||
Defaults to :obj:`None`.
|
||||
|
||||
.. versionadded :: 13.4
|
||||
allowed_updates (list[:obj:`str`], optional): Passed to
|
||||
allowed_updates (Sequence[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.set_webhook`. Defaults to :obj:`None`.
|
||||
|
||||
.. 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``.
|
||||
|
||||
@@ -624,7 +631,7 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
||||
port: int,
|
||||
url_path: str,
|
||||
bootstrap_retries: int,
|
||||
allowed_updates: Optional[list[str]],
|
||||
allowed_updates: Optional[Sequence[str]],
|
||||
cert: Optional[Union[str, Path]] = None,
|
||||
key: Optional[Union[str, Path]] = None,
|
||||
drop_pending_updates: Optional[bool] = None,
|
||||
@@ -767,7 +774,7 @@ class Updater(contextlib.AbstractAsyncContextManager["Updater"]):
|
||||
self,
|
||||
max_retries: int,
|
||||
webhook_url: Optional[str],
|
||||
allowed_updates: Optional[list[str]],
|
||||
allowed_updates: Optional[Sequence[str]],
|
||||
drop_pending_updates: Optional[bool] = None,
|
||||
cert: Optional[bytes] = None,
|
||||
bootstrap_interval: float = 1,
|
||||
|
||||
+7
-4
@@ -36,7 +36,7 @@ from html import escape
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from telegram._utils.types import MarkdownVersion
|
||||
from telegram.constants import MessageType
|
||||
from telegram.constants import MessageLimit, MessageType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Message, Update
|
||||
@@ -173,7 +173,8 @@ def create_deep_linked_url(
|
||||
:obj:`str`: An URL to start the bot with specific parameters.
|
||||
|
||||
Raises:
|
||||
:exc:`ValueError`: If the length of the :paramref:`payload` exceeds 64 characters,
|
||||
:exc:`ValueError`: If the length of the :paramref:`payload` exceeds \
|
||||
:tg-const:`telegram.constants.MessageLimit.DEEP_LINK_LENGTH` characters,
|
||||
contains invalid characters, or if the :paramref:`bot_username` is less than 4
|
||||
characters.
|
||||
"""
|
||||
@@ -184,8 +185,10 @@ def create_deep_linked_url(
|
||||
if not payload:
|
||||
return base_url
|
||||
|
||||
if len(payload) > 64:
|
||||
raise ValueError("The deep-linking payload must not exceed 64 characters.")
|
||||
if len(payload) > MessageLimit.DEEP_LINK_LENGTH:
|
||||
raise ValueError(
|
||||
f"The deep-linking payload must not exceed {MessageLimit.DEEP_LINK_LENGTH} characters."
|
||||
)
|
||||
|
||||
if not re.match(r"^[A-Za-z0-9_-]+$", payload):
|
||||
raise ValueError(
|
||||
|
||||
+28
-1
@@ -24,7 +24,7 @@ import re
|
||||
import pytest
|
||||
|
||||
from telegram import Message, constants
|
||||
from telegram._utils.enum import IntEnum, StringEnum
|
||||
from telegram._utils.enum import FloatEnum, IntEnum, StringEnum
|
||||
from telegram.error import BadRequest
|
||||
from tests.auxil.build_messages import make_message
|
||||
from tests.auxil.files import data_file
|
||||
@@ -41,6 +41,11 @@ class IntEnumTest(IntEnum):
|
||||
BAR = 2
|
||||
|
||||
|
||||
class FloatEnumTest(FloatEnum):
|
||||
FOO = 1.1
|
||||
BAR = 2.1
|
||||
|
||||
|
||||
class TestConstantsWithoutRequest:
|
||||
"""Also test _utils.enum.StringEnum on the fly because tg.constants is currently the only
|
||||
place where that class is used."""
|
||||
@@ -69,6 +74,7 @@ class TestConstantsWithoutRequest:
|
||||
def test_to_json(self):
|
||||
assert json.dumps(StrEnumTest.FOO) == json.dumps("foo")
|
||||
assert json.dumps(IntEnumTest.FOO) == json.dumps(1)
|
||||
assert json.dumps(FloatEnumTest.FOO) == json.dumps(1.1)
|
||||
|
||||
def test_string_representation(self):
|
||||
# test __repr__
|
||||
@@ -90,6 +96,15 @@ class TestConstantsWithoutRequest:
|
||||
# test __str__
|
||||
assert str(IntEnumTest.FOO) == "1"
|
||||
|
||||
def test_float_representation(self):
|
||||
# test __repr__
|
||||
assert repr(FloatEnumTest.FOO) == "<FloatEnumTest.FOO>"
|
||||
# test __format__
|
||||
assert f"{FloatEnumTest.FOO}/0 is undefined!" == "1.1/0 is undefined!"
|
||||
assert f"{FloatEnumTest.FOO:*^10}" == "***1.1****"
|
||||
# test __str__
|
||||
assert str(FloatEnumTest.FOO) == "1.1"
|
||||
|
||||
def test_string_inheritance(self):
|
||||
assert isinstance(StrEnumTest.FOO, str)
|
||||
assert StrEnumTest.FOO + StrEnumTest.BAR == "foobar"
|
||||
@@ -115,6 +130,18 @@ class TestConstantsWithoutRequest:
|
||||
|
||||
assert hash(IntEnumTest.FOO) == hash(1)
|
||||
|
||||
def test_float_inheritance(self):
|
||||
assert isinstance(FloatEnumTest.FOO, float)
|
||||
assert FloatEnumTest.FOO + FloatEnumTest.BAR == 3.2
|
||||
|
||||
assert FloatEnumTest.FOO == FloatEnumTest.FOO
|
||||
assert FloatEnumTest.FOO == 1.1
|
||||
assert FloatEnumTest.FOO != FloatEnumTest.BAR
|
||||
assert FloatEnumTest.FOO != 2.1
|
||||
assert object() != FloatEnumTest.FOO
|
||||
|
||||
assert hash(FloatEnumTest.FOO) == hash(1.1)
|
||||
|
||||
def test_bot_api_version_and_info(self):
|
||||
assert str(constants.BOT_API_VERSION_INFO) == constants.BOT_API_VERSION
|
||||
assert (
|
||||
|
||||
+198
-12
@@ -18,11 +18,13 @@
|
||||
# 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,
|
||||
@@ -34,6 +36,7 @@ from telegram import (
|
||||
StarTransaction,
|
||||
StarTransactions,
|
||||
Sticker,
|
||||
TelegramObject,
|
||||
TransactionPartner,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
@@ -42,6 +45,7 @@ from telegram import (
|
||||
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
|
||||
@@ -67,6 +71,13 @@ def withdrawal_state_pending():
|
||||
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(
|
||||
@@ -97,6 +108,13 @@ def transaction_partner_user():
|
||||
)
|
||||
|
||||
|
||||
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(),
|
||||
@@ -107,6 +125,7 @@ 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(),
|
||||
@@ -126,6 +145,7 @@ def star_transactions():
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
TransactionPartner.AFFILIATE_PROGRAM,
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
@@ -140,6 +160,7 @@ def tp_scope_type(request):
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
@@ -147,6 +168,7 @@ def tp_scope_type(request):
|
||||
TransactionPartnerUser,
|
||||
],
|
||||
ids=[
|
||||
TransactionPartner.AFFILIATE_PROGRAM,
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
@@ -161,6 +183,7 @@ def tp_scope_class(request):
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
(TransactionPartnerAffiliateProgram, TransactionPartner.AFFILIATE_PROGRAM),
|
||||
(TransactionPartnerFragment, TransactionPartner.FRAGMENT),
|
||||
(TransactionPartnerOther, TransactionPartner.OTHER),
|
||||
(TransactionPartnerTelegramAds, TransactionPartner.TELEGRAM_ADS),
|
||||
@@ -168,6 +191,7 @@ def tp_scope_class(request):
|
||||
(TransactionPartnerUser, TransactionPartner.USER),
|
||||
],
|
||||
ids=[
|
||||
TransactionPartner.AFFILIATE_PROGRAM,
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
@@ -188,7 +212,14 @@ def transaction_partner(tp_scope_class_and_type):
|
||||
"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,
|
||||
)
|
||||
@@ -256,6 +287,7 @@ def revenue_withdrawal_state(rws_scope_class_and_type):
|
||||
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(
|
||||
@@ -278,6 +310,7 @@ class TestStarTransactionWithoutRequest(StarTransactionTestBase):
|
||||
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(),
|
||||
@@ -287,6 +320,7 @@ class TestStarTransactionWithoutRequest(StarTransactionTestBase):
|
||||
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
|
||||
@@ -311,6 +345,7 @@ class TestStarTransactionWithoutRequest(StarTransactionTestBase):
|
||||
expected_dict = {
|
||||
"id": "1",
|
||||
"amount": 1,
|
||||
"nanostar_amount": 365,
|
||||
"date": st.date,
|
||||
"source": st.source.to_dict(),
|
||||
"receiver": st.receiver.to_dict(),
|
||||
@@ -401,8 +436,15 @@ class TestStarTransactionsWithoutRequest(StarTransactionsTestBase):
|
||||
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):
|
||||
@@ -421,26 +463,28 @@ class TestTransactionPartnerWithoutRequest(TransactionPartnerTestBase):
|
||||
"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_
|
||||
if "withdrawal_state" in cls.__slots__:
|
||||
assert tp.withdrawal_state == self.withdrawal_state
|
||||
if "user" in cls.__slots__:
|
||||
assert tp.user == self.user
|
||||
assert tp.invoice_payload == self.invoice_payload
|
||||
if "request_count" in cls.__slots__:
|
||||
assert tp.request_count == self.request_count
|
||||
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
|
||||
@@ -451,14 +495,20 @@ class TestTransactionPartnerWithoutRequest(TransactionPartnerTestBase):
|
||||
"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
|
||||
@@ -472,7 +522,9 @@ class TestTransactionPartnerWithoutRequest(TransactionPartnerTestBase):
|
||||
"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
|
||||
|
||||
@@ -481,11 +533,16 @@ class TestTransactionPartnerWithoutRequest(TransactionPartnerTestBase):
|
||||
|
||||
assert isinstance(tp_dict, dict)
|
||||
assert tp_dict["type"] == transaction_partner.type
|
||||
if hasattr(transaction_partner, "user"):
|
||||
assert tp_dict["user"] == transaction_partner.user.to_dict()
|
||||
assert tp_dict["invoice_payload"] == transaction_partner.invoice_payload
|
||||
if hasattr(transaction_partner, "withdrawal_state"):
|
||||
assert tp_dict["withdrawal_state"] == transaction_partner.withdrawal_state.to_dict()
|
||||
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
|
||||
@@ -661,3 +718,132 @@ class TestRevenueWithdrawalStateWithoutRequest(RevenueWithdrawalStateTestBase):
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user