mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-21 14:46:29 +01:00
Co-authored-by: aelkheir <90580077+aelkheir@users.noreply.github.com> Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
This commit is contained in:
parent
9248c539d0
commit
67a97ae5a7
26 changed files with 668 additions and 81 deletions
|
@ -11,7 +11,7 @@
|
|||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-7.9-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-7.10-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API version
|
||||
|
||||
|
@ -81,7 +81,7 @@ After installing_ the library, be sure to check out the section on `working with
|
|||
Telegram API support
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
All types and methods of the Telegram Bot API **7.9** are natively supported by this library.
|
||||
All types and methods of the Telegram Bot API **7.10** are natively supported by this library.
|
||||
In addition, Bot API functionality not yet natively included can still be used as described `in our wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Forward-Compatibility>`_.
|
||||
|
||||
Notable Features
|
||||
|
|
|
@ -120,6 +120,7 @@ Available Types
|
|||
telegram.paidmediainfo
|
||||
telegram.paidmediaphoto
|
||||
telegram.paidmediapreview
|
||||
telegram.paidmediapurchased
|
||||
telegram.paidmediavideo
|
||||
telegram.photosize
|
||||
telegram.poll
|
||||
|
|
|
@ -18,6 +18,7 @@ Handlers
|
|||
telegram.ext.inlinequeryhandler
|
||||
telegram.ext.messagehandler
|
||||
telegram.ext.messagereactionhandler
|
||||
telegram.ext.paidmediapurchasedhandler
|
||||
telegram.ext.pollanswerhandler
|
||||
telegram.ext.pollhandler
|
||||
telegram.ext.precheckoutqueryhandler
|
||||
|
|
6
docs/source/telegram.ext.paidmediapurchasedhandler.rst
Normal file
6
docs/source/telegram.ext.paidmediapurchasedhandler.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
PaidMediaPurchasedHandler
|
||||
=========================
|
||||
|
||||
.. autoclass:: telegram.ext.PaidMediaPurchasedHandler
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.paidmediapurchased.rst
Normal file
6
docs/source/telegram.paidmediapurchased.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
PaidMediaPurchased
|
||||
==================
|
||||
|
||||
.. autoclass:: telegram.PaidMediaPurchased
|
||||
:members:
|
||||
:show-inheritance:
|
|
@ -180,6 +180,7 @@ __all__ = (
|
|||
"PaidMediaInfo",
|
||||
"PaidMediaPhoto",
|
||||
"PaidMediaPreview",
|
||||
"PaidMediaPurchased",
|
||||
"PaidMediaVideo",
|
||||
"PassportData",
|
||||
"PassportElementError",
|
||||
|
@ -419,7 +420,14 @@ from ._messageorigin import (
|
|||
MessageOriginUser,
|
||||
)
|
||||
from ._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
|
||||
from ._paidmedia import PaidMedia, PaidMediaInfo, PaidMediaPhoto, PaidMediaPreview, PaidMediaVideo
|
||||
from ._paidmedia import (
|
||||
PaidMedia,
|
||||
PaidMediaInfo,
|
||||
PaidMediaPhoto,
|
||||
PaidMediaPreview,
|
||||
PaidMediaPurchased,
|
||||
PaidMediaVideo,
|
||||
)
|
||||
from ._passport.credentials import (
|
||||
Credentials,
|
||||
DataCredentials,
|
||||
|
|
|
@ -9193,6 +9193,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
payload: Optional[str] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
|
@ -9211,9 +9212,15 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
Telegram Star proceeds from this media will be credited to the chat's balance.
|
||||
Otherwise, they will be credited to the bot's balance.
|
||||
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access
|
||||
to the media.
|
||||
to the media; :tg-const:`telegram.constants.InvoiceLimit.MIN_STAR_COUNT` -
|
||||
:tg-const:`telegram.constants.InvoiceLimit.MAX_STAR_COUNT`.
|
||||
media (Sequence[:class:`telegram.InputPaidMedia`]): A list describing the media to be
|
||||
sent; up to :tg-const:`telegram.constants.MediaGroupLimit.MAX_MEDIA_LENGTH` items.
|
||||
payload (:obj:`str`, optional): Bot-defined paid media payload,
|
||||
0-:tg-const:`telegram.constants.InvoiceLimit.MAX_PAYLOAD_LENGTH` bytes. This will
|
||||
not be displayed to the user, use it for your internal processes.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
caption (:obj:`str`, optional): Caption of the media to be sent,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters.
|
||||
parse_mode (:obj:`str`, optional): |parse_mode|
|
||||
|
@ -9252,6 +9259,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
"star_count": star_count,
|
||||
"media": media,
|
||||
"show_caption_above_media": show_caption_above_media,
|
||||
"payload": payload,
|
||||
}
|
||||
|
||||
return await self._send_message(
|
||||
|
|
|
@ -3350,6 +3350,7 @@ class _ChatBase(TelegramObject):
|
|||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
payload: Optional[str] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
|
@ -3391,6 +3392,7 @@ class _ChatBase(TelegramObject):
|
|||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
business_connection_id=business_connection_id,
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -187,15 +187,22 @@ class ChatBoostSourceGiftCode(ChatBoostSource):
|
|||
|
||||
class ChatBoostSourceGiveaway(ChatBoostSource):
|
||||
"""
|
||||
The boost was obtained by the creation of a Telegram Premium giveaway. This boosts the chat 4
|
||||
times for the duration of the corresponding Telegram Premium subscription.
|
||||
The boost was obtained by the creation of a Telegram Premium giveaway or a Telegram Star.
|
||||
This boosts the chat 4 times for the duration of the corresponding Telegram Premium
|
||||
subscription for Telegram Premium giveaways and :attr:`prize_star_count` / 500 times for
|
||||
one year for Telegram Star giveaways.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
Args:
|
||||
giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway;
|
||||
the message could have been deleted already. May be 0 if the message isn't sent yet.
|
||||
user (:class:`telegram.User`, optional): User that won the prize in the giveaway if any.
|
||||
user (:class:`telegram.User`, optional): User that won the prize in the giveaway if any;
|
||||
for Telegram Premium giveaways only.
|
||||
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
|
||||
giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
is_unclaimed (:obj:`bool`, optional): :obj:`True`, if the giveaway was completed, but
|
||||
there was no user to win the prize.
|
||||
|
||||
|
@ -205,17 +212,22 @@ class ChatBoostSourceGiveaway(ChatBoostSource):
|
|||
giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway;
|
||||
the message could have been deleted already. May be 0 if the message isn't sent yet.
|
||||
user (:class:`telegram.User`): Optional. User that won the prize in the giveaway if any.
|
||||
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
|
||||
giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
is_unclaimed (:obj:`bool`): Optional. :obj:`True`, if the giveaway was completed, but
|
||||
there was no user to win the prize.
|
||||
"""
|
||||
|
||||
__slots__ = ("giveaway_message_id", "is_unclaimed", "user")
|
||||
__slots__ = ("giveaway_message_id", "is_unclaimed", "prize_star_count", "user")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
giveaway_message_id: int,
|
||||
user: Optional[User] = None,
|
||||
is_unclaimed: Optional[bool] = None,
|
||||
prize_star_count: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -224,6 +236,7 @@ class ChatBoostSourceGiveaway(ChatBoostSource):
|
|||
with self._unfrozen():
|
||||
self.giveaway_message_id: int = giveaway_message_id
|
||||
self.user: Optional[User] = user
|
||||
self.prize_star_count: Optional[int] = prize_star_count
|
||||
self.is_unclaimed: Optional[bool] = is_unclaimed
|
||||
|
||||
|
||||
|
|
|
@ -56,8 +56,13 @@ class Giveaway(TelegramObject):
|
|||
country codes indicating the countries from which eligible users for the giveaway must
|
||||
come. If empty, then all users can participate in the giveaway. Users with a phone
|
||||
number that was bought on Fragment can always participate in giveaways.
|
||||
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
|
||||
giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram
|
||||
Premium subscription won from the giveaway will be active for.
|
||||
Premium subscription won from the giveaway will be active for; for Telegram Premium
|
||||
giveaways only.
|
||||
|
||||
Attributes:
|
||||
chats (Sequence[:class:`telegram.Chat`]): The list of chats which the user must join to
|
||||
|
@ -75,8 +80,13 @@ class Giveaway(TelegramObject):
|
|||
country codes indicating the countries from which eligible users for the giveaway must
|
||||
come. If empty, then all users can participate in the giveaway. Users with a phone
|
||||
number that was bought on Fragment can always participate in giveaways.
|
||||
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
|
||||
giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram
|
||||
Premium subscription won from the giveaway will be active for.
|
||||
Premium subscription won from the giveaway will be active for; for Telegram Premium
|
||||
giveaways only.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
|
@ -86,6 +96,7 @@ class Giveaway(TelegramObject):
|
|||
"only_new_members",
|
||||
"premium_subscription_month_count",
|
||||
"prize_description",
|
||||
"prize_star_count",
|
||||
"winner_count",
|
||||
"winners_selection_date",
|
||||
)
|
||||
|
@ -100,6 +111,7 @@ class Giveaway(TelegramObject):
|
|||
prize_description: Optional[str] = None,
|
||||
country_codes: Optional[Sequence[str]] = None,
|
||||
premium_subscription_month_count: Optional[int] = None,
|
||||
prize_star_count: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -113,6 +125,7 @@ class Giveaway(TelegramObject):
|
|||
self.prize_description: Optional[str] = prize_description
|
||||
self.country_codes: Tuple[str, ...] = parse_sequence_arg(country_codes)
|
||||
self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count
|
||||
self.prize_star_count: Optional[int] = prize_star_count
|
||||
|
||||
self._id_attrs = (
|
||||
self.chats,
|
||||
|
@ -145,13 +158,28 @@ class Giveaway(TelegramObject):
|
|||
|
||||
class GiveawayCreated(TelegramObject):
|
||||
"""This object represents a service message about the creation of a scheduled giveaway.
|
||||
Currently holds no information.
|
||||
|
||||
Args:
|
||||
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be
|
||||
split between giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be
|
||||
split between giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
__slots__ = ("prize_star_count",)
|
||||
|
||||
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
|
||||
def __init__(
|
||||
self, prize_star_count: Optional[int] = None, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.prize_star_count: Optional[int] = prize_star_count
|
||||
|
||||
self._freeze()
|
||||
|
||||
|
@ -173,6 +201,10 @@ class GiveawayWinners(TelegramObject):
|
|||
winner_count (:obj:`int`): Total number of winners in the giveaway
|
||||
winners (Sequence[:class:`telegram.User`]): List of up to
|
||||
:tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway
|
||||
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
|
||||
giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
additional_chat_count (:obj:`int`, optional): The number of other chats the user had to
|
||||
join in order to be eligible for the giveaway
|
||||
premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram
|
||||
|
@ -194,6 +226,10 @@ class GiveawayWinners(TelegramObject):
|
|||
:tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway
|
||||
additional_chat_count (:obj:`int`): Optional. The number of other chats the user had to
|
||||
join in order to be eligible for the giveaway
|
||||
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
|
||||
giveaway winners; for Telegram Star giveaways only.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram
|
||||
Premium subscription won from the giveaway will be active for
|
||||
unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes
|
||||
|
@ -211,6 +247,7 @@ class GiveawayWinners(TelegramObject):
|
|||
"only_new_members",
|
||||
"premium_subscription_month_count",
|
||||
"prize_description",
|
||||
"prize_star_count",
|
||||
"unclaimed_prize_count",
|
||||
"was_refunded",
|
||||
"winner_count",
|
||||
|
@ -231,6 +268,7 @@ class GiveawayWinners(TelegramObject):
|
|||
only_new_members: Optional[bool] = None,
|
||||
was_refunded: Optional[bool] = None,
|
||||
prize_description: Optional[str] = None,
|
||||
prize_star_count: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -247,6 +285,7 @@ class GiveawayWinners(TelegramObject):
|
|||
self.only_new_members: Optional[bool] = only_new_members
|
||||
self.was_refunded: Optional[bool] = was_refunded
|
||||
self.prize_description: Optional[str] = prize_description
|
||||
self.prize_star_count: Optional[int] = prize_star_count
|
||||
|
||||
self._id_attrs = (
|
||||
self.chat,
|
||||
|
@ -295,21 +334,29 @@ class GiveawayCompleted(TelegramObject):
|
|||
unclaimed_prize_count (:obj:`int`, optional): Number of undistributed prizes
|
||||
giveaway_message (:class:`telegram.Message`, optional): Message with the giveaway that was
|
||||
completed, if it wasn't deleted
|
||||
is_star_giveaway (:obj:`bool`, optional): :obj:`True`, if the giveaway is a Telegram Star
|
||||
giveaway. Otherwise, currently, the giveaway is a Telegram Premium giveaway.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
Attributes:
|
||||
winner_count (:obj:`int`): Number of winners in the giveaway
|
||||
unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes
|
||||
giveaway_message (:class:`telegram.Message`): Optional. Message with the giveaway that was
|
||||
completed, if it wasn't deleted
|
||||
is_star_giveaway (:obj:`bool`): Optional. :obj:`True`, if the giveaway is a Telegram Star
|
||||
giveaway. Otherwise, currently, the giveaway is a Telegram Premium giveaway.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ("giveaway_message", "unclaimed_prize_count", "winner_count")
|
||||
__slots__ = ("giveaway_message", "is_star_giveaway", "unclaimed_prize_count", "winner_count")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
winner_count: int,
|
||||
unclaimed_prize_count: Optional[int] = None,
|
||||
giveaway_message: Optional["Message"] = None,
|
||||
is_star_giveaway: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -318,6 +365,7 @@ class GiveawayCompleted(TelegramObject):
|
|||
self.winner_count: int = winner_count
|
||||
self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count
|
||||
self.giveaway_message: Optional[Message] = giveaway_message
|
||||
self.is_star_giveaway: Optional[bool] = is_star_giveaway
|
||||
|
||||
self._id_attrs = (
|
||||
self.winner_count,
|
||||
|
|
|
@ -24,6 +24,7 @@ from telegram import constants
|
|||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._files.video import Video
|
||||
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
|
||||
|
@ -288,3 +289,52 @@ class PaidMediaInfo(TelegramObject):
|
|||
|
||||
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class PaidMediaPurchased(TelegramObject):
|
||||
"""This object contains information about a paid media purchase.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`from_user` and :attr:`paid_media_payload` are equal.
|
||||
|
||||
Note:
|
||||
In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
from_user (:class:`telegram.User`): User who purchased the media.
|
||||
paid_media_payload (:obj:`str`): Bot-specified paid media payload.
|
||||
|
||||
Attributes:
|
||||
from_user (:class:`telegram.User`): User who purchased the media.
|
||||
paid_media_payload (:obj:`str`): Bot-specified paid media payload.
|
||||
"""
|
||||
|
||||
__slots__ = ("from_user", "paid_media_payload")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
from_user: "User",
|
||||
paid_media_payload: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.from_user: User = from_user
|
||||
self.paid_media_payload: str = paid_media_payload
|
||||
|
||||
self._id_attrs = (self.from_user, self.paid_media_payload)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaPurchased"]:
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["from_user"] = User.de_json(data=data.pop("from"), bot=bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
|
|
@ -328,6 +328,9 @@ class TransactionPartnerUser(TransactionPartner):
|
|||
media bought by the user.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
paid_media_payload (:obj:`str`, optional): Optional. Bot-specified paid media payload.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
|
@ -338,19 +341,20 @@ class TransactionPartnerUser(TransactionPartner):
|
|||
media bought by the user.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
paid_media_payload (:obj:`str`): Optional. Optional. Bot-specified paid media payload.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"invoice_payload",
|
||||
"paid_media",
|
||||
"user",
|
||||
)
|
||||
__slots__ = ("invoice_payload", "paid_media", "paid_media_payload", "user")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user: "User",
|
||||
invoice_payload: Optional[str] = None,
|
||||
paid_media: Optional[Sequence[PaidMedia]] = None,
|
||||
paid_media_payload: Optional[str] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
|
@ -360,6 +364,7 @@ class TransactionPartnerUser(TransactionPartner):
|
|||
self.user: User = user
|
||||
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._id_attrs = (
|
||||
self.type,
|
||||
self.user,
|
||||
|
|
|
@ -30,6 +30,7 @@ from telegram._choseninlineresult import ChosenInlineResult
|
|||
from telegram._inline.inlinequery import InlineQuery
|
||||
from telegram._message import Message
|
||||
from telegram._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
|
||||
from telegram._paidmedia import PaidMediaPurchased
|
||||
from telegram._payment.precheckoutquery import PreCheckoutQuery
|
||||
from telegram._payment.shippingquery import ShippingQuery
|
||||
from telegram._poll import Poll, PollAnswer
|
||||
|
@ -156,6 +157,11 @@ class Update(TelegramObject):
|
|||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
purchased_paid_media (:class:`telegram.PaidMediaPurchased`, optional): A user purchased
|
||||
paid media with a non-empty payload sent by the bot in a non-channel chat.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
|
||||
Attributes:
|
||||
update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a
|
||||
|
@ -263,6 +269,11 @@ class Update(TelegramObject):
|
|||
were deleted from a connected business account.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
purchased_paid_media (:class:`telegram.PaidMediaPurchased`): Optional. A user purchased
|
||||
paid media with a non-empty payload sent by the bot in a non-channel chat.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
|
@ -290,6 +301,7 @@ class Update(TelegramObject):
|
|||
"poll",
|
||||
"poll_answer",
|
||||
"pre_checkout_query",
|
||||
"purchased_paid_media",
|
||||
"removed_chat_boost",
|
||||
"shipping_query",
|
||||
"update_id",
|
||||
|
@ -383,6 +395,13 @@ class Update(TelegramObject):
|
|||
""":const:`telegram.constants.UpdateType.DELETED_BUSINESS_MESSAGES`
|
||||
|
||||
.. versionadded:: 21.1"""
|
||||
|
||||
PURCHASED_PAID_MEDIA: Final[str] = constants.UpdateType.PURCHASED_PAID_MEDIA
|
||||
""":const:`telegram.constants.UpdateType.PURCHASED_PAID_MEDIA`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
ALL_TYPES: Final[List[str]] = list(constants.UpdateType)
|
||||
"""List[:obj:`str`]: A list of all available update types.
|
||||
|
||||
|
@ -413,6 +432,7 @@ class Update(TelegramObject):
|
|||
business_message: Optional[Message] = None,
|
||||
edited_business_message: Optional[Message] = None,
|
||||
deleted_business_messages: Optional[BusinessMessagesDeleted] = None,
|
||||
purchased_paid_media: Optional[PaidMediaPurchased] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -444,6 +464,7 @@ class Update(TelegramObject):
|
|||
self.deleted_business_messages: Optional[BusinessMessagesDeleted] = (
|
||||
deleted_business_messages
|
||||
)
|
||||
self.purchased_paid_media: Optional[PaidMediaPurchased] = purchased_paid_media
|
||||
|
||||
self._effective_user: Optional[User] = None
|
||||
self._effective_sender: Optional[Union[User, Chat]] = None
|
||||
|
@ -475,6 +496,9 @@ class Update(TelegramObject):
|
|||
This property now also considers :attr:`business_connection`, :attr:`business_message`
|
||||
and :attr:`edited_business_message`.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
This property now also considers :attr:`purchased_paid_media`.
|
||||
|
||||
Example:
|
||||
* If :attr:`message` is present, this will give
|
||||
:attr:`telegram.Message.from_user`.
|
||||
|
@ -531,6 +555,9 @@ class Update(TelegramObject):
|
|||
elif self.business_connection:
|
||||
user = self.business_connection.user
|
||||
|
||||
elif self.purchased_paid_media:
|
||||
user = self.purchased_paid_media.from_user
|
||||
|
||||
self._effective_user = user
|
||||
return user
|
||||
|
||||
|
@ -601,7 +628,8 @@ class Update(TelegramObject):
|
|||
This is the case, if :attr:`inline_query`,
|
||||
:attr:`chosen_inline_result`, :attr:`callback_query` from inline messages,
|
||||
:attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll`,
|
||||
:attr:`poll_answer`, or :attr:`business_connection` is present.
|
||||
:attr:`poll_answer`, :attr:`business_connection`, or :attr:`purchased_paid_media`
|
||||
is present.
|
||||
|
||||
.. versionchanged:: 21.1
|
||||
This property now also considers :attr:`business_message`,
|
||||
|
@ -768,5 +796,8 @@ class Update(TelegramObject):
|
|||
data["deleted_business_messages"] = BusinessMessagesDeleted.de_json(
|
||||
data.get("deleted_business_messages"), bot
|
||||
)
|
||||
data["purchased_paid_media"] = PaidMediaPurchased.de_json(
|
||||
data.get("purchased_paid_media"), bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
|
|
@ -152,7 +152,7 @@ class _AccentColor(NamedTuple):
|
|||
#: :data:`telegram.__bot_api_version_info__`.
|
||||
#:
|
||||
#: .. versionadded:: 20.0
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=9)
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=10)
|
||||
#: :obj:`str`: Telegram Bot API
|
||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||
#: :data:`telegram.__bot_api_version__`.
|
||||
|
@ -552,6 +552,42 @@ class AccentColor(Enum):
|
|||
"""
|
||||
|
||||
|
||||
class BackgroundTypeType(StringEnum):
|
||||
"""This enum contains the available types of :class:`telegram.BackgroundType`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: 21.2
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
FILL = "fill"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with fill background."""
|
||||
WALLPAPER = "wallpaper"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background."""
|
||||
PATTERN = "pattern"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with pattern background."""
|
||||
CHAT_THEME = "chat_theme"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background."""
|
||||
|
||||
|
||||
class BackgroundFillType(StringEnum):
|
||||
"""This enum contains the available types of :class:`telegram.BackgroundFill`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: 21.2
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
SOLID = "solid"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with solid fill."""
|
||||
GRADIENT = "gradient"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
|
||||
FREEFORM_GRADIENT = "freeform_gradient"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
|
||||
|
||||
|
||||
class BotCommandLimit(IntEnum):
|
||||
"""This enum contains limitations for :class:`telegram.BotCommand` and
|
||||
:meth:`telegram.Bot.set_my_commands`.
|
||||
|
@ -833,6 +869,25 @@ class ChatLimit(IntEnum):
|
|||
"""
|
||||
|
||||
|
||||
class ChatSubscriptionLimit(IntEnum):
|
||||
"""This enum contains limitations for
|
||||
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_period` and
|
||||
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_price`.
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
SUBSCRIPTION_PERIOD = 2592000
|
||||
""":obj:`int`: The number of seconds the subscription will be active."""
|
||||
MIN_PRICE = 1
|
||||
""":obj:`int`: Amount of stars a user pays, minimum amount the subscription can be set to."""
|
||||
MAX_PRICE = 2500
|
||||
""":obj:`int`: Amount of stars a user pays, maximum amount the subscription can be set to."""
|
||||
|
||||
|
||||
class BackgroundTypeLimit(IntEnum):
|
||||
"""This enum contains limitations for :class:`telegram.BackgroundTypeFill`,
|
||||
:class:`telegram.BackgroundTypeWallpaper` and :class:`telegram.BackgroundTypePattern`.
|
||||
|
@ -2724,6 +2779,11 @@ class UpdateType(StringEnum):
|
|||
|
||||
.. versionadded:: 21.1
|
||||
"""
|
||||
PURCHASED_PAID_MEDIA = "purchased_paid_media"
|
||||
""":obj:`str`: Updates with :attr:`telegram.Update.purchased_paid_media`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
|
||||
class InvoiceLimit(IntEnum):
|
||||
|
@ -2795,6 +2855,8 @@ class InvoiceLimit(IntEnum):
|
|||
:meth:`telegram.Bot.send_invoice`.
|
||||
* :paramref:`~telegram.Bot.create_invoice_link.payload` parameter of
|
||||
:meth:`telegram.Bot.create_invoice_link`.
|
||||
* :paramref:`~telegram.Bot.send_paid_media.payload` parameter of
|
||||
:meth:`telegram.Bot.send_paid_media`.
|
||||
"""
|
||||
MAX_TIP_AMOUNTS = 4
|
||||
""":obj:`int`: Maximum length of a :obj:`Sequence` passed as:
|
||||
|
@ -2804,6 +2866,20 @@ class InvoiceLimit(IntEnum):
|
|||
* :paramref:`~telegram.Bot.create_invoice_link.suggested_tip_amounts` parameter of
|
||||
:meth:`telegram.Bot.create_invoice_link`.
|
||||
"""
|
||||
MIN_STAR_COUNT = 1
|
||||
""":obj:`int`: Minimum amount of starts that must be paid to buy access to a paid media
|
||||
passed as :paramref:`~telegram.Bot.send_paid_media.star_count` parameter of
|
||||
:meth:`telegram.Bot.send_paid_media`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
MAX_STAR_COUNT = 2500
|
||||
""":obj:`int`: Maximum amount of starts that must be paid to buy access to a paid media
|
||||
passed as :paramref:`~telegram.Bot.send_paid_media.star_count` parameter of
|
||||
:meth:`telegram.Bot.send_paid_media`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
|
||||
class UserProfilePhotosLimit(IntEnum):
|
||||
|
@ -3066,58 +3142,3 @@ class ReactionEmoji(StringEnum):
|
|||
""":obj:`str`: Woman Shrugging"""
|
||||
POUTING_FACE = "😡"
|
||||
""":obj:`str`: Pouting face"""
|
||||
|
||||
|
||||
class BackgroundTypeType(StringEnum):
|
||||
"""This enum contains the available types of :class:`telegram.BackgroundType`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: 21.2
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
FILL = "fill"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with fill background."""
|
||||
WALLPAPER = "wallpaper"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background."""
|
||||
PATTERN = "pattern"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with pattern background."""
|
||||
CHAT_THEME = "chat_theme"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background."""
|
||||
|
||||
|
||||
class BackgroundFillType(StringEnum):
|
||||
"""This enum contains the available types of :class:`telegram.BackgroundFill`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: 21.2
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
SOLID = "solid"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with solid fill."""
|
||||
GRADIENT = "gradient"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
|
||||
FREEFORM_GRADIENT = "freeform_gradient"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
|
||||
|
||||
|
||||
class ChatSubscriptionLimit(IntEnum):
|
||||
"""This enum contains limitations for
|
||||
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_period` and
|
||||
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_price`.
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: 21.5
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
SUBSCRIPTION_PERIOD = 2592000
|
||||
""":obj:`int`: The number of seconds the subscription will be active."""
|
||||
MIN_PRICE = 1
|
||||
""":obj:`int`: Amount of stars a user pays, minimum amount the subscription can be set to."""
|
||||
MAX_PRICE = 2500
|
||||
""":obj:`int`: Amount of stars a user pays, maximum amount the subscription can be set to."""
|
||||
|
|
|
@ -48,6 +48,7 @@ __all__ = (
|
|||
"JobQueue",
|
||||
"MessageHandler",
|
||||
"MessageReactionHandler",
|
||||
"PaidMediaPurchasedHandler",
|
||||
"PersistenceInput",
|
||||
"PicklePersistence",
|
||||
"PollAnswerHandler",
|
||||
|
@ -89,6 +90,7 @@ from ._handlers.conversationhandler import ConversationHandler
|
|||
from ._handlers.inlinequeryhandler import InlineQueryHandler
|
||||
from ._handlers.messagehandler import MessageHandler
|
||||
from ._handlers.messagereactionhandler import MessageReactionHandler
|
||||
from ._handlers.paidmediapurchasedhandler import PaidMediaPurchasedHandler
|
||||
from ._handlers.pollanswerhandler import PollAnswerHandler
|
||||
from ._handlers.pollhandler import PollHandler
|
||||
from ._handlers.precheckoutqueryhandler import PreCheckoutQueryHandler
|
||||
|
|
|
@ -4235,6 +4235,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
payload: Optional[str] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
|
@ -4265,6 +4266,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
business_connection_id=business_connection_id,
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
async def create_chat_subscription_invite_link(
|
||||
|
|
95
telegram/ext/_handlers/paidmediapurchasedhandler.py
Normal file
95
telegram/ext/_handlers/paidmediapurchasedhandler.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2024
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the PaidMediaPurchased class."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from telegram import Update
|
||||
from telegram._utils.defaultvalue import DEFAULT_TRUE
|
||||
from telegram._utils.types import SCT, DVType
|
||||
from telegram.ext._handlers.basehandler import BaseHandler
|
||||
from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
|
||||
from telegram.ext._utils.types import CCT, RT, HandlerCallback
|
||||
|
||||
|
||||
class PaidMediaPurchasedHandler(BaseHandler[Update, CCT, RT]):
|
||||
"""Handler class to handle Telegram
|
||||
:attr:`purchased paid media <telegram.Update.purchased_paid_media>`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
callback (:term:`coroutine function`): The callback function for this handler. Will be
|
||||
called when :meth:`check_update` has determined that an update should be processed by
|
||||
this handler. Callback signature::
|
||||
|
||||
async def callback(update: Update, context: CallbackContext)
|
||||
user_id (:obj:`int` | Collection[:obj:`int`], optional): Filters requests to allow only
|
||||
those which are from the specified user ID(s).
|
||||
|
||||
username (:obj:`str` | Collection[:obj:`str`], optional): Filters requests to allow only
|
||||
those which are from the specified username(s).
|
||||
|
||||
block (:obj:`bool`, optional): Determines whether the return value of the callback should
|
||||
be awaited before processing the next handler in
|
||||
:meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`.
|
||||
|
||||
.. seealso:: :wiki:`Concurrency`
|
||||
Attributes:
|
||||
callback (:term:`coroutine function`): The callback function for this handler.
|
||||
block (:obj:`bool`): Determines whether the return value of the callback should be
|
||||
awaited before processing the next handler in
|
||||
:meth:`telegram.ext.Application.process_update`.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_user_ids",
|
||||
"_usernames",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self: "PaidMediaPurchasedHandler[CCT, RT]",
|
||||
callback: HandlerCallback[Update, CCT, RT],
|
||||
user_id: Optional[SCT[int]] = None,
|
||||
username: Optional[SCT[str]] = None,
|
||||
block: DVType[bool] = DEFAULT_TRUE,
|
||||
):
|
||||
super().__init__(callback, block=block)
|
||||
|
||||
self._user_ids = parse_chat_id(user_id)
|
||||
self._usernames = parse_username(username)
|
||||
|
||||
def check_update(self, update: object) -> bool:
|
||||
"""Determines whether an update should be passed to this handler's :attr:`callback`.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
|
||||
"""
|
||||
if not isinstance(update, Update) or not update.purchased_paid_media:
|
||||
return False
|
||||
|
||||
if not self._user_ids and not self._usernames:
|
||||
return True
|
||||
if update.purchased_paid_media.from_user.id in self._user_ids:
|
||||
return True
|
||||
return update.purchased_paid_media.from_user.username in self._usernames
|
|
@ -72,7 +72,7 @@ complete and correct. To run it, export an environment variable first:
|
|||
|
||||
$ export TEST_OFFICIAL=true
|
||||
|
||||
and then run ``pytest tests/test_official.py``. Note: You need py 3.10+ to run this test.
|
||||
and then run ``pytest tests/test_official/test_official.py``. Note: You need py 3.10+ to run this test.
|
||||
|
||||
We also have another marker, ``@pytest.mark.dev``, which you can add to tests that you want to run selectively.
|
||||
Use as follows:
|
||||
|
|
169
tests/ext/test_paidmediapurchasedhandler.py
Normal file
169
tests/ext/test_paidmediapurchasedhandler.py
Normal file
|
@ -0,0 +1,169 @@
|
|||
#!/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 asyncio
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
Bot,
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
ChosenInlineResult,
|
||||
Message,
|
||||
PaidMediaPurchased,
|
||||
PreCheckoutQuery,
|
||||
ShippingQuery,
|
||||
Update,
|
||||
User,
|
||||
)
|
||||
from telegram._utils.datetime import UTC
|
||||
from telegram.ext import CallbackContext, JobQueue, PaidMediaPurchasedHandler
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text")
|
||||
|
||||
params = [
|
||||
{"message": message},
|
||||
{"edited_message": message},
|
||||
{"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)},
|
||||
{"channel_post": message},
|
||||
{"edited_channel_post": message},
|
||||
{"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")},
|
||||
{"shipping_query": ShippingQuery("id", User(1, "", False), "", None)},
|
||||
{"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")},
|
||||
{"callback_query": CallbackQuery(1, User(1, "", False), "chat")},
|
||||
]
|
||||
|
||||
ids = (
|
||||
"message",
|
||||
"edited_message",
|
||||
"callback_query",
|
||||
"channel_post",
|
||||
"edited_channel_post",
|
||||
"chosen_inline_result",
|
||||
"shipping_query",
|
||||
"pre_checkout_query",
|
||||
"callback_query_without_message",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class", params=params, ids=ids)
|
||||
def false_update(request):
|
||||
return Update(update_id=2, **request.param)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def time():
|
||||
return datetime.datetime.now(tz=UTC)
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def purchased_paid_media(bot):
|
||||
bc = PaidMediaPurchased(
|
||||
from_user=User(1, "name", username="user_a", is_bot=False),
|
||||
paid_media_payload="payload",
|
||||
)
|
||||
bc.set_bot(bot)
|
||||
return bc
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def purchased_paid_media_update(bot, purchased_paid_media):
|
||||
return Update(0, purchased_paid_media=purchased_paid_media)
|
||||
|
||||
|
||||
class TestPaidMediaPurchasedHandler:
|
||||
test_flag = False
|
||||
|
||||
def test_slot_behaviour(self):
|
||||
action = PaidMediaPurchasedHandler(self.callback)
|
||||
for attr in action.__slots__:
|
||||
assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot"
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset(self):
|
||||
self.test_flag = False
|
||||
|
||||
async def callback(self, update, context):
|
||||
self.test_flag = (
|
||||
isinstance(context, CallbackContext)
|
||||
and isinstance(context.bot, Bot)
|
||||
and isinstance(update, Update)
|
||||
and isinstance(context.update_queue, asyncio.Queue)
|
||||
and isinstance(context.job_queue, JobQueue)
|
||||
and isinstance(context.user_data, dict)
|
||||
and isinstance(context.bot_data, dict)
|
||||
and isinstance(
|
||||
update.purchased_paid_media,
|
||||
PaidMediaPurchased,
|
||||
)
|
||||
)
|
||||
|
||||
def test_with_user_id(self, purchased_paid_media_update):
|
||||
handler = PaidMediaPurchasedHandler(self.callback, user_id=1)
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, user_id=[1])
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, user_id=2, username="@user_a")
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
|
||||
handler = PaidMediaPurchasedHandler(self.callback, user_id=2)
|
||||
assert not handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, user_id=[2])
|
||||
assert not handler.check_update(purchased_paid_media_update)
|
||||
|
||||
def test_with_username(self, purchased_paid_media_update):
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username="user_a")
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username="@user_a")
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username=["user_a"])
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username=["@user_a"])
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, user_id=1, username="@user_b")
|
||||
assert handler.check_update(purchased_paid_media_update)
|
||||
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username="user_b")
|
||||
assert not handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username="@user_b")
|
||||
assert not handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username=["user_b"])
|
||||
assert not handler.check_update(purchased_paid_media_update)
|
||||
handler = PaidMediaPurchasedHandler(self.callback, username=["@user_b"])
|
||||
assert not handler.check_update(purchased_paid_media_update)
|
||||
|
||||
purchased_paid_media_update.purchased_paid_media.from_user._unfreeze()
|
||||
purchased_paid_media_update.purchased_paid_media.from_user.username = None
|
||||
assert not handler.check_update(purchased_paid_media_update)
|
||||
|
||||
def test_other_update_types(self, false_update):
|
||||
handler = PaidMediaPurchasedHandler(self.callback)
|
||||
assert not handler.check_update(false_update)
|
||||
assert not handler.check_update(True)
|
||||
|
||||
async def test_context(self, app, purchased_paid_media_update):
|
||||
handler = PaidMediaPurchasedHandler(callback=self.callback)
|
||||
app.add_handler(handler)
|
||||
|
||||
async with app:
|
||||
await app.process_update(purchased_paid_media_update)
|
||||
assert self.test_flag
|
|
@ -1299,6 +1299,7 @@ class TestChatWithoutRequest(ChatTestBase):
|
|||
and kwargs["media"] == "media"
|
||||
and kwargs["star_count"] == 42
|
||||
and kwargs["caption"] == "stars"
|
||||
and kwargs["payload"] == "payload"
|
||||
)
|
||||
|
||||
assert check_shortcut_signature(Chat.send_paid_media, Bot.send_paid_media, ["chat_id"], [])
|
||||
|
@ -1306,7 +1307,9 @@ class TestChatWithoutRequest(ChatTestBase):
|
|||
assert await check_defaults_handling(chat.send_paid_media, chat.get_bot())
|
||||
|
||||
monkeypatch.setattr(chat.get_bot(), "send_paid_media", make_assertion)
|
||||
assert await chat.send_paid_media(media="media", star_count=42, caption="stars")
|
||||
assert await chat.send_paid_media(
|
||||
media="media", star_count=42, caption="stars", payload="payload"
|
||||
)
|
||||
|
||||
def test_mention_html(self):
|
||||
chat = Chat(id=1, type="foo")
|
||||
|
|
|
@ -50,6 +50,7 @@ class ChatBoostDefaults:
|
|||
user = User(1, "user", False)
|
||||
date = to_timestamp(datetime.datetime.utcnow())
|
||||
default_source = ChatBoostSourcePremium(user)
|
||||
prize_star_count = 99
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
@ -91,6 +92,7 @@ def chat_boost_source_giveaway():
|
|||
user=ChatBoostDefaults.user,
|
||||
giveaway_message_id=ChatBoostDefaults.giveaway_message_id,
|
||||
is_unclaimed=ChatBoostDefaults.is_unclaimed,
|
||||
prize_star_count=ChatBoostDefaults.prize_star_count,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ def giveaway():
|
|||
premium_subscription_month_count=(
|
||||
TestGiveawayWithoutRequest.premium_subscription_month_count
|
||||
),
|
||||
prize_star_count=TestGiveawayWithoutRequest.prize_star_count,
|
||||
)
|
||||
|
||||
|
||||
|
@ -60,6 +61,7 @@ class TestGiveawayWithoutRequest:
|
|||
prize_description = "prize_description"
|
||||
country_codes = ["DE", "US"]
|
||||
premium_subscription_month_count = 3
|
||||
prize_star_count = 99
|
||||
|
||||
def test_slot_behaviour(self, giveaway):
|
||||
for attr in giveaway.__slots__:
|
||||
|
@ -76,6 +78,7 @@ class TestGiveawayWithoutRequest:
|
|||
"prize_description": self.prize_description,
|
||||
"country_codes": self.country_codes,
|
||||
"premium_subscription_month_count": self.premium_subscription_month_count,
|
||||
"prize_star_count": self.prize_star_count,
|
||||
}
|
||||
|
||||
giveaway = Giveaway.de_json(json_dict, offline_bot)
|
||||
|
@ -89,6 +92,7 @@ class TestGiveawayWithoutRequest:
|
|||
assert giveaway.prize_description == self.prize_description
|
||||
assert giveaway.country_codes == tuple(self.country_codes)
|
||||
assert giveaway.premium_subscription_month_count == self.premium_subscription_month_count
|
||||
assert giveaway.prize_star_count == self.prize_star_count
|
||||
|
||||
assert Giveaway.de_json(None, offline_bot) is None
|
||||
|
||||
|
@ -102,6 +106,7 @@ class TestGiveawayWithoutRequest:
|
|||
"prize_description": self.prize_description,
|
||||
"country_codes": self.country_codes,
|
||||
"premium_subscription_month_count": self.premium_subscription_month_count,
|
||||
"prize_star_count": self.prize_star_count,
|
||||
}
|
||||
|
||||
giveaway_raw = Giveaway.de_json(json_dict, raw_bot)
|
||||
|
@ -133,6 +138,7 @@ class TestGiveawayWithoutRequest:
|
|||
giveaway_dict["premium_subscription_month_count"]
|
||||
== self.premium_subscription_month_count
|
||||
)
|
||||
assert giveaway_dict["prize_star_count"] == self.prize_star_count
|
||||
|
||||
def test_equality(self, giveaway):
|
||||
a = giveaway
|
||||
|
@ -164,15 +170,40 @@ class TestGiveawayWithoutRequest:
|
|||
assert hash(a) != hash(e)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def giveaway_created():
|
||||
return GiveawayCreated(
|
||||
prize_star_count=TestGiveawayCreatedWithoutRequest.prize_star_count,
|
||||
)
|
||||
|
||||
|
||||
class TestGiveawayCreatedWithoutRequest:
|
||||
def test_slot_behaviour(self):
|
||||
giveaway_created = GiveawayCreated()
|
||||
prize_star_count = 99
|
||||
|
||||
def test_slot_behaviour(self, giveaway_created):
|
||||
for attr in giveaway_created.__slots__:
|
||||
assert getattr(giveaway_created, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(giveaway_created)) == len(
|
||||
set(mro_slots(giveaway_created))
|
||||
), "duplicate slot"
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
"prize_star_count": self.prize_star_count,
|
||||
}
|
||||
|
||||
gac = GiveawayCreated.de_json(json_dict, bot)
|
||||
assert gac.api_kwargs == {}
|
||||
assert gac.prize_star_count == self.prize_star_count
|
||||
|
||||
assert Giveaway.de_json(None, bot) is None
|
||||
|
||||
def test_to_dict(self, giveaway_created):
|
||||
gac_dict = giveaway_created.to_dict()
|
||||
|
||||
assert isinstance(gac_dict, dict)
|
||||
assert gac_dict["prize_star_count"] == self.prize_star_count
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def giveaway_winners():
|
||||
|
@ -190,6 +221,7 @@ def giveaway_winners():
|
|||
additional_chat_count=TestGiveawayWinnersWithoutRequest.additional_chat_count,
|
||||
unclaimed_prize_count=TestGiveawayWinnersWithoutRequest.unclaimed_prize_count,
|
||||
was_refunded=TestGiveawayWinnersWithoutRequest.was_refunded,
|
||||
prize_star_count=TestGiveawayWinnersWithoutRequest.prize_star_count,
|
||||
)
|
||||
|
||||
|
||||
|
@ -205,6 +237,7 @@ class TestGiveawayWinnersWithoutRequest:
|
|||
only_new_members = True
|
||||
was_refunded = True
|
||||
prize_description = "prize_description"
|
||||
prize_star_count = 99
|
||||
|
||||
def test_slot_behaviour(self, giveaway_winners):
|
||||
for attr in giveaway_winners.__slots__:
|
||||
|
@ -226,6 +259,7 @@ class TestGiveawayWinnersWithoutRequest:
|
|||
"only_new_members": self.only_new_members,
|
||||
"was_refunded": self.was_refunded,
|
||||
"prize_description": self.prize_description,
|
||||
"prize_star_count": self.prize_star_count,
|
||||
}
|
||||
|
||||
giveaway_winners = GiveawayWinners.de_json(json_dict, offline_bot)
|
||||
|
@ -245,6 +279,7 @@ class TestGiveawayWinnersWithoutRequest:
|
|||
assert giveaway_winners.only_new_members == self.only_new_members
|
||||
assert giveaway_winners.was_refunded == self.was_refunded
|
||||
assert giveaway_winners.prize_description == self.prize_description
|
||||
assert giveaway_winners.prize_star_count == self.prize_star_count
|
||||
|
||||
assert GiveawayWinners.de_json(None, offline_bot) is None
|
||||
|
||||
|
@ -291,6 +326,7 @@ class TestGiveawayWinnersWithoutRequest:
|
|||
assert giveaway_winners_dict["only_new_members"] == self.only_new_members
|
||||
assert giveaway_winners_dict["was_refunded"] == self.was_refunded
|
||||
assert giveaway_winners_dict["prize_description"] == self.prize_description
|
||||
assert giveaway_winners_dict["prize_star_count"] == self.prize_star_count
|
||||
|
||||
def test_equality(self, giveaway_winners):
|
||||
a = giveaway_winners
|
||||
|
@ -336,12 +372,14 @@ def giveaway_completed():
|
|||
winner_count=TestGiveawayCompletedWithoutRequest.winner_count,
|
||||
unclaimed_prize_count=TestGiveawayCompletedWithoutRequest.unclaimed_prize_count,
|
||||
giveaway_message=TestGiveawayCompletedWithoutRequest.giveaway_message,
|
||||
is_star_giveaway=TestGiveawayCompletedWithoutRequest.is_star_giveaway,
|
||||
)
|
||||
|
||||
|
||||
class TestGiveawayCompletedWithoutRequest:
|
||||
winner_count = 42
|
||||
unclaimed_prize_count = 4
|
||||
is_star_giveaway = True
|
||||
giveaway_message = Message(
|
||||
message_id=1,
|
||||
date=dtm.datetime.now(dtm.timezone.utc),
|
||||
|
@ -362,6 +400,7 @@ class TestGiveawayCompletedWithoutRequest:
|
|||
"winner_count": self.winner_count,
|
||||
"unclaimed_prize_count": self.unclaimed_prize_count,
|
||||
"giveaway_message": self.giveaway_message.to_dict(),
|
||||
"is_star_giveaway": self.is_star_giveaway,
|
||||
}
|
||||
|
||||
giveaway_completed = GiveawayCompleted.de_json(json_dict, offline_bot)
|
||||
|
@ -370,6 +409,7 @@ class TestGiveawayCompletedWithoutRequest:
|
|||
assert giveaway_completed.winner_count == self.winner_count
|
||||
assert giveaway_completed.unclaimed_prize_count == self.unclaimed_prize_count
|
||||
assert giveaway_completed.giveaway_message == self.giveaway_message
|
||||
assert giveaway_completed.is_star_giveaway == self.is_star_giveaway
|
||||
|
||||
assert GiveawayCompleted.de_json(None, offline_bot) is None
|
||||
|
||||
|
@ -380,6 +420,7 @@ class TestGiveawayCompletedWithoutRequest:
|
|||
assert giveaway_completed_dict["winner_count"] == self.winner_count
|
||||
assert giveaway_completed_dict["unclaimed_prize_count"] == self.unclaimed_prize_count
|
||||
assert giveaway_completed_dict["giveaway_message"] == self.giveaway_message.to_dict()
|
||||
assert giveaway_completed_dict["is_star_giveaway"] == self.is_star_giveaway
|
||||
|
||||
def test_equality(self, giveaway_completed):
|
||||
a = giveaway_completed
|
||||
|
@ -387,6 +428,7 @@ class TestGiveawayCompletedWithoutRequest:
|
|||
winner_count=self.winner_count,
|
||||
unclaimed_prize_count=self.unclaimed_prize_count,
|
||||
giveaway_message=self.giveaway_message,
|
||||
is_star_giveaway=self.is_star_giveaway,
|
||||
)
|
||||
c = GiveawayCompleted(
|
||||
winner_count=self.winner_count + 30,
|
||||
|
|
|
@ -236,7 +236,7 @@ def message(bot):
|
|||
winner_count=5,
|
||||
)
|
||||
},
|
||||
{"giveaway_created": GiveawayCreated()},
|
||||
{"giveaway_created": GiveawayCreated(prize_star_count=99)},
|
||||
{
|
||||
"giveaway_winners": GiveawayWinners(
|
||||
chat=Chat(1, Chat.CHANNEL),
|
||||
|
|
|
@ -27,8 +27,10 @@ from telegram import (
|
|||
PaidMediaInfo,
|
||||
PaidMediaPhoto,
|
||||
PaidMediaPreview,
|
||||
PaidMediaPurchased,
|
||||
PaidMediaVideo,
|
||||
PhotoSize,
|
||||
User,
|
||||
Video,
|
||||
)
|
||||
from telegram.constants import PaidMediaType
|
||||
|
@ -122,6 +124,14 @@ def paid_media_info():
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def paid_media_purchased():
|
||||
return PaidMediaPurchased(
|
||||
from_user=PaidMediaPurchasedTestBase.from_user,
|
||||
paid_media_payload=PaidMediaPurchasedTestBase.paid_media_payload,
|
||||
)
|
||||
|
||||
|
||||
class PaidMediaTestBase:
|
||||
width = 640
|
||||
height = 480
|
||||
|
@ -323,3 +333,54 @@ class TestPaidMediaInfoWithoutRequest(PaidMediaInfoTestBase):
|
|||
|
||||
assert pmi1 != pmi3
|
||||
assert hash(pmi1) != hash(pmi3)
|
||||
|
||||
|
||||
class PaidMediaPurchasedTestBase:
|
||||
from_user = User(1, "user", False)
|
||||
paid_media_payload = "payload"
|
||||
|
||||
|
||||
class TestPaidMediaPurchasedWithoutRequest(PaidMediaPurchasedTestBase):
|
||||
def test_slot_behaviour(self, paid_media_purchased):
|
||||
inst = paid_media_purchased
|
||||
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, bot):
|
||||
json_dict = {
|
||||
"from": self.from_user.to_dict(),
|
||||
"paid_media_payload": self.paid_media_payload,
|
||||
}
|
||||
pmp = PaidMediaPurchased.de_json(json_dict, bot)
|
||||
pmp_none = PaidMediaPurchased.de_json(None, bot)
|
||||
assert pmp.from_user == self.from_user
|
||||
assert pmp.paid_media_payload == self.paid_media_payload
|
||||
assert pmp.api_kwargs == {}
|
||||
assert pmp_none is None
|
||||
|
||||
def test_to_dict(self, paid_media_purchased):
|
||||
assert paid_media_purchased.to_dict() == {
|
||||
"from": self.from_user.to_dict(),
|
||||
"paid_media_payload": self.paid_media_payload,
|
||||
}
|
||||
|
||||
def test_equality(self):
|
||||
pmp1 = PaidMediaPurchased(
|
||||
from_user=self.from_user,
|
||||
paid_media_payload=self.paid_media_payload,
|
||||
)
|
||||
pmp2 = PaidMediaPurchased(
|
||||
from_user=self.from_user,
|
||||
paid_media_payload=self.paid_media_payload,
|
||||
)
|
||||
pmp3 = PaidMediaPurchased(
|
||||
from_user=User(2, "user", False),
|
||||
paid_media_payload="other",
|
||||
)
|
||||
|
||||
assert pmp1 == pmp2
|
||||
assert hash(pmp1) == hash(pmp2)
|
||||
|
||||
assert pmp1 != pmp3
|
||||
assert hash(pmp1) != hash(pmp3)
|
||||
|
|
|
@ -74,6 +74,7 @@ def transaction_partner_user():
|
|||
]
|
||||
)
|
||||
],
|
||||
paid_media_payload="payload",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ from telegram import (
|
|||
Message,
|
||||
MessageReactionCountUpdated,
|
||||
MessageReactionUpdated,
|
||||
PaidMediaPurchased,
|
||||
Poll,
|
||||
PollAnswer,
|
||||
PollOption,
|
||||
|
@ -143,6 +144,11 @@ business_message = Message(
|
|||
User(1, "", False),
|
||||
)
|
||||
|
||||
purchased_paid_media = PaidMediaPurchased(
|
||||
from_user=User(1, "", False),
|
||||
paid_media_payload="payload",
|
||||
)
|
||||
|
||||
|
||||
params = [
|
||||
{"message": message},
|
||||
|
@ -178,6 +184,7 @@ params = [
|
|||
{"deleted_business_messages": deleted_business_messages},
|
||||
{"business_message": business_message},
|
||||
{"edited_business_message": business_message},
|
||||
{"purchased_paid_media": purchased_paid_media},
|
||||
# Must be last to conform with `ids` below!
|
||||
{"callback_query": CallbackQuery(1, User(1, "", False), "chat")},
|
||||
]
|
||||
|
@ -205,6 +212,7 @@ all_types = (
|
|||
"deleted_business_messages",
|
||||
"business_message",
|
||||
"edited_business_message",
|
||||
"purchased_paid_media",
|
||||
)
|
||||
|
||||
ids = (*all_types, "callback_query_without_message")
|
||||
|
@ -290,6 +298,7 @@ class TestUpdateWithoutRequest(UpdateTestBase):
|
|||
or update.poll is not None
|
||||
or update.poll_answer is not None
|
||||
or update.business_connection is not None
|
||||
or update.purchased_paid_media is not None
|
||||
):
|
||||
assert chat.id == 1
|
||||
else:
|
||||
|
@ -403,6 +412,7 @@ class TestUpdateWithoutRequest(UpdateTestBase):
|
|||
or update.message_reaction_count is not None
|
||||
or update.deleted_business_messages is not None
|
||||
or update.business_connection is not None
|
||||
or update.purchased_paid_media is not None
|
||||
):
|
||||
assert eff_message.message_id == message.message_id
|
||||
else:
|
||||
|
|
Loading…
Reference in a new issue