mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-01-06 02:35:53 +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",
|
"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 . import _version, constants, error, helpers, request, warnings
|
||||||
from ._birthdate import Birthdate
|
from ._birthdate import Birthdate
|
||||||
from ._bot import Bot
|
from ._bot import Bot
|
||||||
|
@ -470,21 +481,12 @@ from ._payment.refundedpayment import RefundedPayment
|
||||||
from ._payment.shippingaddress import ShippingAddress
|
from ._payment.shippingaddress import ShippingAddress
|
||||||
from ._payment.shippingoption import ShippingOption
|
from ._payment.shippingoption import ShippingOption
|
||||||
from ._payment.shippingquery import ShippingQuery
|
from ._payment.shippingquery import ShippingQuery
|
||||||
from ._payment.stars import (
|
from ._payment.stars.affiliateinfo import AffiliateInfo
|
||||||
AffiliateInfo,
|
from ._payment.stars.revenuewithdrawalstate import (
|
||||||
RevenueWithdrawalState,
|
RevenueWithdrawalState,
|
||||||
RevenueWithdrawalStateFailed,
|
RevenueWithdrawalStateFailed,
|
||||||
RevenueWithdrawalStatePending,
|
RevenueWithdrawalStatePending,
|
||||||
RevenueWithdrawalStateSucceeded,
|
RevenueWithdrawalStateSucceeded,
|
||||||
StarTransaction,
|
|
||||||
StarTransactions,
|
|
||||||
TransactionPartner,
|
|
||||||
TransactionPartnerAffiliateProgram,
|
|
||||||
TransactionPartnerFragment,
|
|
||||||
TransactionPartnerOther,
|
|
||||||
TransactionPartnerTelegramAds,
|
|
||||||
TransactionPartnerTelegramApi,
|
|
||||||
TransactionPartnerUser,
|
|
||||||
)
|
)
|
||||||
from ._payment.successfulpayment import SuccessfulPayment
|
from ._payment.successfulpayment import SuccessfulPayment
|
||||||
from ._poll import InputPollOption, Poll, PollAnswer, PollOption
|
from ._poll import InputPollOption, Poll, PollAnswer, PollOption
|
||||||
|
|
|
@ -81,7 +81,7 @@ from telegram._inline.preparedinlinemessage import PreparedInlineMessage
|
||||||
from telegram._menubutton import MenuButton
|
from telegram._menubutton import MenuButton
|
||||||
from telegram._message import Message
|
from telegram._message import Message
|
||||||
from telegram._messageid import MessageId
|
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._poll import InputPollOption, Poll
|
||||||
from telegram._reaction import ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
from telegram._reaction import ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||||
from telegram._reply import ReplyParameters
|
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