Thanks to visit codestin.com
Credit goes to github.com

Skip to content

API 7.10 - PaidMediaPurchased #4460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/telegram.at-tree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Available Types
telegram.paidmediainfo
telegram.paidmediaphoto
telegram.paidmediapreview
telegram.paidmediapurchased
telegram.paidmediavideo
telegram.photosize
telegram.poll
Expand Down
6 changes: 6 additions & 0 deletions docs/source/telegram.paidmediapurchased.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
PaidMediaPurchased
==================

.. autoclass:: telegram.PaidMediaPurchased
:members:
:show-inheritance:
10 changes: 9 additions & 1 deletion telegram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
"PaidMediaInfo",
"PaidMediaPhoto",
"PaidMediaPreview",
"PaidMediaPurchased",
"PaidMediaVideo",
"PassportData",
"PassportElementError",
Expand Down Expand Up @@ -419,7 +420,14 @@
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,
Expand Down
50 changes: 50 additions & 0 deletions telegram/_paidmedia.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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
Expand Down Expand Up @@ -288,3 +289,52 @@ def de_json(

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)
33 changes: 32 additions & 1 deletion telegram/_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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__ = (
Expand Down Expand Up @@ -290,6 +301,7 @@ class Update(TelegramObject):
"poll",
"poll_answer",
"pre_checkout_query",
"purchased_paid_media",
"removed_chat_boost",
"shipping_query",
"update_id",
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -413,6 +432,7 @@ def __init__(
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,
):
Expand Down Expand Up @@ -444,6 +464,7 @@ def __init__(
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
Expand Down Expand Up @@ -475,6 +496,9 @@ def effective_user(self) -> Optional["User"]:
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`.
Expand Down Expand Up @@ -531,6 +555,9 @@ def effective_user(self) -> Optional["User"]:
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

Expand Down Expand Up @@ -601,7 +628,8 @@ def effective_chat(self) -> Optional["Chat"]:
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`,
Expand Down Expand Up @@ -768,5 +796,8 @@ def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optio
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)
5 changes: 5 additions & 0 deletions telegram/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2724,6 +2724,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):
Expand Down
2 changes: 2 additions & 0 deletions telegram/ext/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"JobQueue",
"MessageHandler",
"MessageReactionHandler",
"PaidMediaPurchasedHandler",
"PersistenceInput",
"PicklePersistence",
"PollAnswerHandler",
Expand Down Expand Up @@ -89,6 +90,7 @@
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
Expand Down
97 changes: 97 additions & 0 deletions telegram/ext/_handlers/paidmediapurchasedhandler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <[email protected]>
#
# 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, TypeVar

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, HandlerCallback

RT = TypeVar("RT")


class PaidMediaPurchasedHandler(BaseHandler[Update, CCT]):
"""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,
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
Loading
Loading