mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-01-04 02:01:48 +01:00
Refactor Module Structure and Tests for Star Payments Classes (#4615)
This commit is contained in:
parent
4f255b6e21
commit
df20e49db1
14 changed files with 1962 additions and 1656 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:: 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
|
||||
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 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:: 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 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:: 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,)
|
||||
|
||||
|
||||
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)
|
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,)
|
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)
|
|
@ -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 as dtm
|
||||
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=dtm.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=dtm.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(dtm.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(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):
|
||||
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(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(), 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, dtm.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 = 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_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(dtm.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)
|
Loading…
Reference in a new issue