mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-10-23 17:36:26 +02:00
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
This commit is contained in:
parent
98bed6f01a
commit
dba7866aab
47 changed files with 1470 additions and 120 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.5-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-7.6-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API version
|
||||
|
||||
|
@ -79,7 +79,7 @@ make the development of bots easy and straightforward. These classes are contain
|
|||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **7.5** are supported.
|
||||
All types and methods of the Telegram Bot API **7.6** are supported.
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
- Used for sending media grouped together
|
||||
* - :meth:`~telegram.Bot.send_message`
|
||||
- Used for sending text messages
|
||||
* - :meth:`~telegram.Bot.send_paid_media`
|
||||
- Used for sending paid media to channels
|
||||
* - :meth:`~telegram.Bot.send_photo`
|
||||
- Used for sending photos
|
||||
* - :meth:`~telegram.Bot.send_poll`
|
||||
|
|
|
@ -88,6 +88,9 @@ Available Types
|
|||
telegram.inputmediadocument
|
||||
telegram.inputmediaphoto
|
||||
telegram.inputmediavideo
|
||||
telegram.inputpaidmedia
|
||||
telegram.inputpaidmediaphoto
|
||||
telegram.inputpaidmediavideo
|
||||
telegram.inputpolloption
|
||||
telegram.inputsticker
|
||||
telegram.keyboardbutton
|
||||
|
@ -113,6 +116,11 @@ Available Types
|
|||
telegram.messageoriginuser
|
||||
telegram.messagereactioncountupdated
|
||||
telegram.messagereactionupdated
|
||||
telegram.paidmedia
|
||||
telegram.paidmediainfo
|
||||
telegram.paidmediaphoto
|
||||
telegram.paidmediapreview
|
||||
telegram.paidmediavideo
|
||||
telegram.photosize
|
||||
telegram.poll
|
||||
telegram.pollanswer
|
||||
|
@ -125,22 +133,12 @@ Available Types
|
|||
telegram.replykeyboardmarkup
|
||||
telegram.replykeyboardremove
|
||||
telegram.replyparameters
|
||||
telegram.revenuewithdrawalstate
|
||||
telegram.revenuewithdrawalstatefailed
|
||||
telegram.revenuewithdrawalstatepending
|
||||
telegram.revenuewithdrawalstatesucceeded
|
||||
telegram.sentwebappmessage
|
||||
telegram.shareduser
|
||||
telegram.startransaction
|
||||
telegram.startransactions
|
||||
telegram.story
|
||||
telegram.switchinlinequerychosenchat
|
||||
telegram.telegramobject
|
||||
telegram.textquote
|
||||
telegram.transactionpartner
|
||||
telegram.transactionpartnerfragment
|
||||
telegram.transactionpartnerother
|
||||
telegram.transactionpartneruser
|
||||
telegram.update
|
||||
telegram.user
|
||||
telegram.userchatboosts
|
||||
|
|
6
docs/source/telegram.inputpaidmedia.rst
Normal file
6
docs/source/telegram.inputpaidmedia.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
InputPaidMedia
|
||||
==============
|
||||
|
||||
.. autoclass:: telegram.InputPaidMedia
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.inputpaidmediaphoto.rst
Normal file
6
docs/source/telegram.inputpaidmediaphoto.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
InputPaidMediaPhoto
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.InputPaidMediaPhoto
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.inputpaidmediavideo.rst
Normal file
6
docs/source/telegram.inputpaidmediavideo.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
InputPaidMediaVideo
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.InputPaidMediaVideo
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.paidmedia.rst
Normal file
6
docs/source/telegram.paidmedia.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
PaidMedia
|
||||
=========
|
||||
|
||||
.. autoclass:: telegram.PaidMedia
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.paidmediainfo.rst
Normal file
6
docs/source/telegram.paidmediainfo.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
PaidMediaInfo
|
||||
=============
|
||||
|
||||
.. autoclass:: telegram.PaidMediaInfo
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.paidmediaphoto.rst
Normal file
6
docs/source/telegram.paidmediaphoto.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
PaidMediaPhoto
|
||||
==============
|
||||
|
||||
.. autoclass:: telegram.PaidMediaPhoto
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.paidmediapreview.rst
Normal file
6
docs/source/telegram.paidmediapreview.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
PaidMediaPreview
|
||||
================
|
||||
|
||||
.. autoclass:: telegram.PaidMediaPreview
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.paidmediavideo.rst
Normal file
6
docs/source/telegram.paidmediavideo.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
PaidMediaVideo
|
||||
==============
|
||||
|
||||
.. autoclass:: telegram.PaidMediaVideo
|
||||
:members:
|
||||
:show-inheritance:
|
|
@ -8,7 +8,18 @@ Payments
|
|||
telegram.labeledprice
|
||||
telegram.orderinfo
|
||||
telegram.precheckoutquery
|
||||
telegram.revenuewithdrawalstate
|
||||
telegram.revenuewithdrawalstatefailed
|
||||
telegram.revenuewithdrawalstatepending
|
||||
telegram.revenuewithdrawalstatesucceeded
|
||||
telegram.shippingaddress
|
||||
telegram.shippingoption
|
||||
telegram.shippingquery
|
||||
telegram.startransaction
|
||||
telegram.startransactions
|
||||
telegram.successfulpayment
|
||||
telegram.transactionpartner
|
||||
telegram.transactionpartnerfragment
|
||||
telegram.transactionpartnerother
|
||||
telegram.transactionpartnertelegramads
|
||||
telegram.transactionpartneruser
|
||||
|
|
7
docs/source/telegram.transactionpartnertelegramads.rst
Normal file
7
docs/source/telegram.transactionpartnertelegramads.rst
Normal file
|
@ -0,0 +1,7 @@
|
|||
TransactionPartnerTelegramAds
|
||||
=============================
|
||||
|
||||
.. autoclass:: telegram.TransactionPartnerTelegramAds
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members: TelegramObject
|
|
@ -142,6 +142,9 @@ __all__ = (
|
|||
"InputMediaPhoto",
|
||||
"InputMediaVideo",
|
||||
"InputMessageContent",
|
||||
"InputPaidMedia",
|
||||
"InputPaidMediaPhoto",
|
||||
"InputPaidMediaVideo",
|
||||
"InputPollOption",
|
||||
"InputSticker",
|
||||
"InputTextMessageContent",
|
||||
|
@ -173,6 +176,11 @@ __all__ = (
|
|||
"MessageReactionCountUpdated",
|
||||
"MessageReactionUpdated",
|
||||
"OrderInfo",
|
||||
"PaidMedia",
|
||||
"PaidMediaInfo",
|
||||
"PaidMediaPhoto",
|
||||
"PaidMediaPreview",
|
||||
"PaidMediaVideo",
|
||||
"PassportData",
|
||||
"PassportElementError",
|
||||
"PassportElementErrorDataField",
|
||||
|
@ -223,6 +231,7 @@ __all__ = (
|
|||
"TransactionPartner",
|
||||
"TransactionPartnerFragment",
|
||||
"TransactionPartnerOther",
|
||||
"TransactionPartnerTelegramAds",
|
||||
"TransactionPartnerUser",
|
||||
"Update",
|
||||
"User",
|
||||
|
@ -333,6 +342,9 @@ from ._files.inputmedia import (
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputPaidMedia,
|
||||
InputPaidMediaPhoto,
|
||||
InputPaidMediaVideo,
|
||||
)
|
||||
from ._files.inputsticker import InputSticker
|
||||
from ._files.location import Location
|
||||
|
@ -405,6 +417,7 @@ from ._messageorigin import (
|
|||
MessageOriginUser,
|
||||
)
|
||||
from ._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
|
||||
from ._paidmedia import PaidMedia, PaidMediaInfo, PaidMediaPhoto, PaidMediaPreview, PaidMediaVideo
|
||||
from ._passport.credentials import (
|
||||
Credentials,
|
||||
DataCredentials,
|
||||
|
@ -436,16 +449,7 @@ from ._payment.precheckoutquery import PreCheckoutQuery
|
|||
from ._payment.shippingaddress import ShippingAddress
|
||||
from ._payment.shippingoption import ShippingOption
|
||||
from ._payment.shippingquery import ShippingQuery
|
||||
from ._payment.successfulpayment import SuccessfulPayment
|
||||
from ._poll import InputPollOption, Poll, PollAnswer, PollOption
|
||||
from ._proximityalerttriggered import ProximityAlertTriggered
|
||||
from ._reaction import ReactionCount, ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||
from ._reply import ExternalReplyInfo, ReplyParameters, TextQuote
|
||||
from ._replykeyboardmarkup import ReplyKeyboardMarkup
|
||||
from ._replykeyboardremove import ReplyKeyboardRemove
|
||||
from ._sentwebappmessage import SentWebAppMessage
|
||||
from ._shared import ChatShared, SharedUser, UsersShared
|
||||
from ._stars import (
|
||||
from ._payment.stars import (
|
||||
RevenueWithdrawalState,
|
||||
RevenueWithdrawalStateFailed,
|
||||
RevenueWithdrawalStatePending,
|
||||
|
@ -455,8 +459,18 @@ from ._stars import (
|
|||
TransactionPartner,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
TransactionPartnerUser,
|
||||
)
|
||||
from ._payment.successfulpayment import SuccessfulPayment
|
||||
from ._poll import InputPollOption, Poll, PollAnswer, PollOption
|
||||
from ._proximityalerttriggered import ProximityAlertTriggered
|
||||
from ._reaction import ReactionCount, ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||
from ._reply import ExternalReplyInfo, ReplyParameters, TextQuote
|
||||
from ._replykeyboardmarkup import ReplyKeyboardMarkup
|
||||
from ._replykeyboardremove import ReplyKeyboardRemove
|
||||
from ._sentwebappmessage import SentWebAppMessage
|
||||
from ._shared import ChatShared, SharedUser, UsersShared
|
||||
from ._story import Story
|
||||
from ._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
|
||||
from ._telegramobject import TelegramObject
|
||||
|
|
115
telegram/_bot.py
115
telegram/_bot.py
|
@ -70,7 +70,7 @@ from telegram._files.chatphoto import ChatPhoto
|
|||
from telegram._files.contact import Contact
|
||||
from telegram._files.document import Document
|
||||
from telegram._files.file import File
|
||||
from telegram._files.inputmedia import InputMedia
|
||||
from telegram._files.inputmedia import InputMedia, InputPaidMedia
|
||||
from telegram._files.location import Location
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._files.sticker import MaskPosition, Sticker, StickerSet
|
||||
|
@ -84,11 +84,11 @@ from telegram._inline.inlinequeryresultsbutton import InlineQueryResultsButton
|
|||
from telegram._menubutton import MenuButton
|
||||
from telegram._message import Message
|
||||
from telegram._messageid import MessageId
|
||||
from telegram._payment.stars import StarTransactions
|
||||
from telegram._poll import InputPollOption, Poll
|
||||
from telegram._reaction import ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||
from telegram._reply import ReplyParameters
|
||||
from telegram._sentwebappmessage import SentWebAppMessage
|
||||
from telegram._stars import StarTransactions
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._update import Update
|
||||
from telegram._user import User
|
||||
|
@ -578,13 +578,16 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
with new._unfrozen():
|
||||
new.parse_mode = DefaultValue.get_value(new.parse_mode)
|
||||
data[key] = new
|
||||
elif key == "media" and isinstance(val, Sequence):
|
||||
elif (
|
||||
key == "media"
|
||||
and isinstance(val, Sequence)
|
||||
and not isinstance(val[0], InputPaidMedia)
|
||||
):
|
||||
# Copy objects as not to edit them in-place
|
||||
copy_list = [copy.copy(media) for media in val]
|
||||
for media in copy_list:
|
||||
with media._unfrozen():
|
||||
media.parse_mode = DefaultValue.get_value(media.parse_mode)
|
||||
|
||||
data[key] = copy_list
|
||||
# 2)
|
||||
else:
|
||||
|
@ -7654,7 +7657,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> MessageId:
|
||||
"""Use this method to copy messages of any kind. Service messages and invoice messages
|
||||
"""Use this method to copy messages of any kind. Service messages, paid media messages,
|
||||
giveaway messages, giveaway winners messages, and invoice messages
|
||||
can't be copied. The method is analogous to the method :meth:`forward_message`, but the
|
||||
copied message doesn't have a link to the original message.
|
||||
|
||||
|
@ -7780,11 +7784,12 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
) -> Tuple["MessageId", ...]:
|
||||
"""
|
||||
Use this method to copy messages of any kind. If some of the specified messages can't be
|
||||
found or copied, they are skipped. Service messages, giveaway messages, giveaway winners
|
||||
messages, and invoice messages can't be copied. A quiz poll can be copied only if the value
|
||||
of the field correct_option_id is known to the bot. The method is analogous to the method
|
||||
:meth:`forward_messages`, but the copied messages don't have a link to the original
|
||||
message. Album grouping is kept for copied messages.
|
||||
found or copied, they are skipped. Service messages, paid media messages, giveaway
|
||||
messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can
|
||||
be copied only if the value
|
||||
of the field :attr:`telegram.Poll.correct_option_id` is known to the bot. The method is
|
||||
analogous to the method :meth:`forward_messages`, but the copied messages don't have a
|
||||
link to the original message. Album grouping is kept for copied messages.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
|
@ -9163,6 +9168,94 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
bot=self,
|
||||
)
|
||||
|
||||
async def send_paid_media(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
star_count: int,
|
||||
media: Sequence["InputPaidMedia"],
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> Message:
|
||||
"""Use this method to send paid media to channel chats.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
|
||||
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access
|
||||
to the media.
|
||||
media (Sequence[:class:`telegram.InputPaidMedia`]): A list describing the media to be
|
||||
sent; up to :tg-const:`telegram.constants.MediaGroupLimit.MAX_MEDIA_LENGTH` items.
|
||||
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|
|
||||
caption_entities (Sequence[:class:`telegram.MessageEntity`], optional):
|
||||
|caption_entities|
|
||||
show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med|
|
||||
disable_notification (:obj:`bool`, optional): |disable_notification|
|
||||
protect_content (:obj:`bool`, optional): |protect_content|
|
||||
reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters|
|
||||
reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \
|
||||
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
|
||||
Additional interface options. An object for an inline keyboard, custom reply
|
||||
keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
|
||||
Keyword Args:
|
||||
allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply|
|
||||
Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience
|
||||
parameter for
|
||||
|
||||
reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id|
|
||||
Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience
|
||||
parameter for
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, the sent message is returned.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
|
||||
data: JSONDict = {
|
||||
"chat_id": chat_id,
|
||||
"star_count": star_count,
|
||||
"media": media,
|
||||
"show_caption_above_media": show_caption_above_media,
|
||||
}
|
||||
|
||||
return await self._send_message(
|
||||
"sendPaidMedia",
|
||||
data,
|
||||
caption=caption,
|
||||
parse_mode=parse_mode,
|
||||
caption_entities=caption_entities,
|
||||
disable_notification=disable_notification,
|
||||
protect_content=protect_content,
|
||||
reply_parameters=reply_parameters,
|
||||
reply_markup=reply_markup,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict: # noqa: ARG002
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name}
|
||||
|
@ -9417,3 +9510,5 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
"""Alias for :meth:`refund_star_payment`"""
|
||||
getStarTransactions = get_star_transactions
|
||||
"""Alias for :meth:`get_star_transactions`"""
|
||||
sendPaidMedia = send_paid_media
|
||||
"""Alias for :meth:`send_paid_media`"""
|
||||
|
|
|
@ -48,6 +48,7 @@ if TYPE_CHECKING:
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputPaidMedia,
|
||||
InputPollOption,
|
||||
LabeledPrice,
|
||||
LinkPreviewOptions,
|
||||
|
@ -3257,6 +3258,60 @@ class _ChatBase(TelegramObject):
|
|||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
async def send_paid_media(
|
||||
self,
|
||||
star_count: int,
|
||||
media: Sequence["InputPaidMedia"],
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> "Message":
|
||||
"""Shortcut for::
|
||||
|
||||
await bot.send_paid_media(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.send_paid_media`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, instance representing the message posted.
|
||||
"""
|
||||
return await self.get_bot().send_paid_media(
|
||||
chat_id=self.id,
|
||||
star_count=star_count,
|
||||
media=media,
|
||||
caption=caption,
|
||||
parse_mode=parse_mode,
|
||||
caption_entities=caption_entities,
|
||||
show_caption_above_media=show_caption_above_media,
|
||||
disable_notification=disable_notification,
|
||||
protect_content=protect_content,
|
||||
reply_parameters=reply_parameters,
|
||||
reply_markup=reply_markup,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
|
||||
class Chat(_ChatBase):
|
||||
"""This object represents a chat.
|
||||
|
|
|
@ -195,6 +195,10 @@ class ChatFullInfo(_ChatBase):
|
|||
chats.
|
||||
location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which
|
||||
the supergroup is connected.
|
||||
can_send_paid_media (:obj:`bool`, optional): :obj:`True`, if paid media messages can be
|
||||
sent or forwarded to the channel chat. The field is available only for channel chats.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
id (:obj:`int`): Unique identifier for this chat.
|
||||
|
@ -345,6 +349,10 @@ class ChatFullInfo(_ChatBase):
|
|||
chats.
|
||||
location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which
|
||||
the supergroup is connected.
|
||||
can_send_paid_media (:obj:`bool`): Optional. :obj:`True`, if paid media messages can be
|
||||
sent or forwarded to the channel chat. The field is available only for channel chats.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. _accent colors: https://core.telegram.org/bots/api#accent-colors
|
||||
.. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups
|
||||
|
@ -360,6 +368,7 @@ class ChatFullInfo(_ChatBase):
|
|||
"business_intro",
|
||||
"business_location",
|
||||
"business_opening_hours",
|
||||
"can_send_paid_media",
|
||||
"can_set_sticker_set",
|
||||
"custom_emoji_sticker_set_name",
|
||||
"description",
|
||||
|
@ -434,6 +443,7 @@ class ChatFullInfo(_ChatBase):
|
|||
custom_emoji_sticker_set_name: Optional[str] = None,
|
||||
linked_chat_id: Optional[int] = None,
|
||||
location: Optional[ChatLocation] = None,
|
||||
can_send_paid_media: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -496,6 +506,7 @@ class ChatFullInfo(_ChatBase):
|
|||
self.business_intro: Optional[BusinessIntro] = business_intro
|
||||
self.business_location: Optional[BusinessLocation] = business_location
|
||||
self.business_opening_hours: Optional[BusinessOpeningHours] = business_opening_hours
|
||||
self.can_send_paid_media: Optional[bool] = can_send_paid_media
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
|
|
|
@ -44,7 +44,7 @@ class _BaseThumbedMedium(_BaseMedium):
|
|||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`, optional): File size.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Thumbnail as defined by sender.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Thumbnail as defined by the sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
|
@ -54,7 +54,7 @@ class _BaseThumbedMedium(_BaseMedium):
|
|||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`): Optional. File size.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Thumbnail as defined by sender.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Thumbnail as defined by the sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
|
|
|
@ -39,11 +39,11 @@ class Animation(_BaseThumbedMedium):
|
|||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
width (:obj:`int`): Video width as defined by sender.
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
file_name (:obj:`str`, optional): Original animation filename as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
|
||||
width (:obj:`int`): Video width as defined by the sender.
|
||||
height (:obj:`int`): Video height as defined by the sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by the sender.
|
||||
file_name (:obj:`str`, optional): Original animation filename as defined by the sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Animation thumbnail as defined by
|
||||
sender.
|
||||
|
@ -56,11 +56,11 @@ class Animation(_BaseThumbedMedium):
|
|||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
width (:obj:`int`): Video width as defined by sender.
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
file_name (:obj:`str`): Optional. Original animation filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
|
||||
width (:obj:`int`): Video width as defined by the sender.
|
||||
height (:obj:`int`): Video height as defined by the sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by the sender.
|
||||
file_name (:obj:`str`): Optional. Original animation filename as defined by the sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Animation thumbnail as defined by
|
||||
sender.
|
||||
|
|
|
@ -39,12 +39,12 @@ class Audio(_BaseThumbedMedium):
|
|||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
|
||||
the same over time and for different bots. Can't be used to download or reuse the file.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
|
||||
performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio
|
||||
tags.
|
||||
title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by the sender.
|
||||
performer (:obj:`str`, optional): Performer of the audio as defined by the sender or by
|
||||
audio tags.
|
||||
title (:obj:`str`, optional): Title of the audio as defined by the sender or by audio tags.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by the sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Thumbnail of the album cover to
|
||||
which the music file belongs.
|
||||
|
@ -56,12 +56,12 @@ class Audio(_BaseThumbedMedium):
|
|||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
|
||||
the same over time and for different bots. Can't be used to download or reuse the file.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
|
||||
performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio
|
||||
tags.
|
||||
title (:obj:`str`): Optional. Title of the audio as defined by sender or by audio tags.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by the sender.
|
||||
performer (:obj:`str`): Optional. Performer of the audio as defined by the sender or by
|
||||
audio tags.
|
||||
title (:obj:`str`): Optional. Title of the audio as defined by the sender or by audio tags.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by the sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Thumbnail of the album cover to
|
||||
which the music file belongs.
|
||||
|
|
|
@ -39,10 +39,11 @@ class Document(_BaseThumbedMedium):
|
|||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
|
||||
the same over time and for different bots. Can't be used to download or reuse the file.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by the sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by sender.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by the
|
||||
sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
|
@ -51,10 +52,11 @@ class Document(_BaseThumbedMedium):
|
|||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be
|
||||
the same over time and for different bots. Can't be used to download or reuse the file.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by the sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Document thumbnail as defined by sender.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Document thumbnail as defined by the
|
||||
sender.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""Base class for Telegram InputMedia Objects."""
|
||||
from typing import Optional, Sequence, Tuple, Union
|
||||
from typing import Final, Optional, Sequence, Tuple, Union
|
||||
|
||||
from telegram import constants
|
||||
from telegram._files.animation import Animation
|
||||
|
@ -115,13 +115,162 @@ class InputMedia(TelegramObject):
|
|||
)
|
||||
|
||||
|
||||
class InputPaidMedia(TelegramObject):
|
||||
"""
|
||||
Base class for Telegram InputPaidMedia Objects. Currently, it can be one of:
|
||||
|
||||
* :class:`telegram.InputMediaPhoto`
|
||||
* :class:`telegram.InputMediaVideo`
|
||||
|
||||
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of media that the instance represents.
|
||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||
:class:`telegram.PhotoSize` | :class:`telegram.Video`): File to send. |fileinputnopath|
|
||||
Lastly you can pass an existing telegram media object of the corresponding type
|
||||
to send.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the input media.
|
||||
media (:obj:`str` | :class:`telegram.InputFile`): Media to send.
|
||||
"""
|
||||
|
||||
PHOTO: Final[str] = constants.InputPaidMediaType.PHOTO
|
||||
""":const:`telegram.constants.InputPaidMediaType.PHOTO`"""
|
||||
VIDEO: Final[str] = constants.InputPaidMediaType.VIDEO
|
||||
""":const:`telegram.constants.InputPaidMediaType.VIDEO`"""
|
||||
|
||||
__slots__ = ("media", "type")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str, # pylint: disable=redefined-builtin
|
||||
media: Union[str, InputFile, PhotoSize, Video],
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: str = enum.get_member(constants.InputPaidMediaType, type, type)
|
||||
self.media: Union[str, InputFile, PhotoSize, Video] = media
|
||||
|
||||
self._freeze()
|
||||
|
||||
|
||||
class InputPaidMediaPhoto(InputPaidMedia):
|
||||
"""The paid media to send is a photo.
|
||||
|
||||
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||
:class:`telegram.PhotoSize`): File to send. |fileinputnopath|
|
||||
Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the media, always
|
||||
:tg-const:`telegram.constants.InputPaidMediaType.PHOTO`.
|
||||
media (:obj:`str` | :class:`telegram.InputFile`): Photo to send.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
media: Union[FileInput, PhotoSize],
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
media = parse_file_input(media, PhotoSize, attach=True, local_mode=True)
|
||||
super().__init__(type=InputPaidMedia.PHOTO, media=media, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class InputPaidMediaVideo(InputPaidMedia):
|
||||
"""
|
||||
The paid media to send is a video.
|
||||
|
||||
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Note:
|
||||
* When using a :class:`telegram.Video` for the :attr:`media` attribute, it will take the
|
||||
width, height and duration from that video, unless otherwise specified with the optional
|
||||
arguments.
|
||||
* :paramref:`thumbnail` will be ignored for small video files, for which Telegram can
|
||||
easily generate thumbnails. However, this behaviour is undocumented and might be
|
||||
changed by Telegram.
|
||||
|
||||
Args:
|
||||
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
|
||||
:class:`telegram.Video`): File to send. |fileinputnopath|
|
||||
Lastly you can pass an existing :class:`telegram.Video` object to send.
|
||||
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): |thumbdocstringnopath|
|
||||
width (:obj:`int`, optional): Video width.
|
||||
height (:obj:`int`, optional): Video height.
|
||||
duration (:obj:`int`, optional): Video duration in seconds.
|
||||
supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is
|
||||
suitable for streaming.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the media, always
|
||||
:tg-const:`telegram.constants.InputPaidMediaType.VIDEO`.
|
||||
media (:obj:`str` | :class:`telegram.InputFile`): Video to send.
|
||||
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
width (:obj:`int`): Optional. Video width.
|
||||
height (:obj:`int`): Optional. Video height.
|
||||
duration (:obj:`int`): Optional. Video duration in seconds.
|
||||
supports_streaming (:obj:`bool`): Optional. :obj:`True`, if the uploaded video is
|
||||
suitable for streaming.
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "height", "supports_streaming", "thumbnail", "width")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
media: Union[FileInput, Video],
|
||||
thumbnail: Optional[FileInput] = None,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
duration: Optional[int] = None,
|
||||
supports_streaming: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
if isinstance(media, Video):
|
||||
width = width if width is not None else media.width
|
||||
height = height if height is not None else media.height
|
||||
duration = duration if duration is not None else media.duration
|
||||
media = media.file_id
|
||||
else:
|
||||
# We use local_mode=True because we don't have access to the actual setting and want
|
||||
# things to work in local mode.
|
||||
media = parse_file_input(media, attach=True, local_mode=True)
|
||||
|
||||
super().__init__(type=InputPaidMedia.VIDEO, media=media, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.thumbnail: Optional[Union[str, InputFile]] = InputMedia._parse_thumbnail_input(
|
||||
thumbnail
|
||||
)
|
||||
self.width: Optional[int] = width
|
||||
self.height: Optional[int] = height
|
||||
self.duration: Optional[int] = duration
|
||||
self.supports_streaming: Optional[bool] = supports_streaming
|
||||
|
||||
|
||||
class InputMediaAnimation(InputMedia):
|
||||
"""Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent.
|
||||
|
||||
Note:
|
||||
When using a :class:`telegram.Animation` for the :attr:`media` attribute, it will take the
|
||||
width, height and duration from that video, unless otherwise specified with the optional
|
||||
arguments.
|
||||
width, height and duration from that animation, unless otherwise specified with the
|
||||
optional arguments.
|
||||
|
||||
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
|
||||
|
||||
|
@ -510,10 +659,10 @@ class InputMediaAudio(InputMedia):
|
|||
.. versionchanged:: 20.0
|
||||
|sequenceclassargs|
|
||||
|
||||
duration (:obj:`int`, optional): Duration of the audio in seconds as defined by sender.
|
||||
performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio
|
||||
tags.
|
||||
title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags.
|
||||
duration (:obj:`int`, optional): Duration of the audio in seconds as defined by the sender.
|
||||
performer (:obj:`str`, optional): Performer of the audio as defined by the sender or by
|
||||
audio tags.
|
||||
title (:obj:`str`, optional): Title of the audio as defined by the sender or by audio tags.
|
||||
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): |thumbdocstringnopath|
|
||||
|
||||
|
@ -533,9 +682,9 @@ class InputMediaAudio(InputMedia):
|
|||
* |tupleclassattrs|
|
||||
* |alwaystuple|
|
||||
duration (:obj:`int`): Optional. Duration of the audio in seconds.
|
||||
performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio
|
||||
tags.
|
||||
title (:obj:`str`): Optional. Title of the audio as defined by sender or by audio tags.
|
||||
performer (:obj:`str`): Optional. Performer of the audio as defined by the sender or by
|
||||
audio tags.
|
||||
title (:obj:`str`): Optional. Title of the audio as defined by the sender or by audio tags.
|
||||
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
|
|
@ -32,8 +32,8 @@ class Location(TelegramObject):
|
|||
considered equal, if their :attr:`longitude` and :attr:`latitude` are equal.
|
||||
|
||||
Args:
|
||||
longitude (:obj:`float`): Longitude as defined by sender.
|
||||
latitude (:obj:`float`): Latitude as defined by sender.
|
||||
longitude (:obj:`float`): Longitude as defined by the sender.
|
||||
latitude (:obj:`float`): Latitude as defined by the sender.
|
||||
horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location,
|
||||
measured in meters; 0-:tg-const:`telegram.Location.HORIZONTAL_ACCURACY`.
|
||||
live_period (:obj:`int`, optional): Time relative to the message sending date, during which
|
||||
|
@ -45,8 +45,8 @@ class Location(TelegramObject):
|
|||
approaching another chat member, in meters. For sent live locations only.
|
||||
|
||||
Attributes:
|
||||
longitude (:obj:`float`): Longitude as defined by sender.
|
||||
latitude (:obj:`float`): Latitude as defined by sender.
|
||||
longitude (:obj:`float`): Longitude as defined by the sender.
|
||||
latitude (:obj:`float`): Latitude as defined by the sender.
|
||||
horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location,
|
||||
measured in meters; 0-:tg-const:`telegram.Location.HORIZONTAL_ACCURACY`.
|
||||
live_period (:obj:`int`): Optional. Time relative to the message sending date, during which
|
||||
|
|
|
@ -39,11 +39,11 @@ class Video(_BaseThumbedMedium):
|
|||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
width (:obj:`int`): Video width as defined by sender.
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of a file as defined by sender.
|
||||
width (:obj:`int`): Video width as defined by the sender.
|
||||
height (:obj:`int`): Video height as defined by the sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by the sender.
|
||||
file_name (:obj:`str`, optional): Original filename as defined by the sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of a file as defined by the sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Video thumbnail.
|
||||
|
||||
|
@ -55,11 +55,11 @@ class Video(_BaseThumbedMedium):
|
|||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
width (:obj:`int`): Video width as defined by sender.
|
||||
height (:obj:`int`): Video height as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of a file as defined by sender.
|
||||
width (:obj:`int`): Video width as defined by the sender.
|
||||
height (:obj:`int`): Video height as defined by the sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by the sender.
|
||||
file_name (:obj:`str`): Optional. Original filename as defined by the sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of a file as defined by the sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class VideoNote(_BaseThumbedMedium):
|
|||
Can't be used to download or reuse the file.
|
||||
length (:obj:`int`): Video width and height (diameter of the video message) as defined
|
||||
by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by the sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Video thumbnail.
|
||||
|
||||
|
@ -56,7 +56,7 @@ class VideoNote(_BaseThumbedMedium):
|
|||
Can't be used to download or reuse the file.
|
||||
length (:obj:`int`): Video width and height (diameter of the video message) as defined
|
||||
by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
|
||||
duration (:obj:`int`): Duration of the video in seconds as defined by the sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@ class Voice(_BaseMedium):
|
|||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by the sender.
|
||||
mime_type (:obj:`str`, optional): MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`, optional): File size in bytes.
|
||||
|
||||
Attributes:
|
||||
|
@ -45,8 +45,8 @@ class Voice(_BaseMedium):
|
|||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
|
||||
duration (:obj:`int`): Duration of the audio in seconds as defined by the sender.
|
||||
mime_type (:obj:`str`): Optional. MIME type of the file as defined by the sender.
|
||||
file_size (:obj:`int`): Optional. File size in bytes.
|
||||
|
||||
"""
|
||||
|
|
|
@ -145,7 +145,10 @@ class MenuButtonWebApp(MenuButton):
|
|||
web_app (:class:`telegram.WebAppInfo`): Description of the Web App that will be launched
|
||||
when the user presses the button. The Web App will be able to send an arbitrary
|
||||
message on behalf of the user using the method :meth:`~telegram.Bot.answerWebAppQuery`
|
||||
of :class:`~telegram.Bot`.
|
||||
of :class:`~telegram.Bot`. Alternatively, a ``t.me`` link to a Web App of the bot can
|
||||
be specified in the object instead of the Web App's URL, in which case the Web App
|
||||
will be opened as if the user pressed the link.
|
||||
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :tg-const:`telegram.constants.MenuButtonType.WEB_APP`.
|
||||
|
@ -153,7 +156,9 @@ class MenuButtonWebApp(MenuButton):
|
|||
web_app (:class:`telegram.WebAppInfo`): Description of the Web App that will be launched
|
||||
when the user presses the button. The Web App will be able to send an arbitrary
|
||||
message on behalf of the user using the method :meth:`~telegram.Bot.answerWebAppQuery`
|
||||
of :class:`~telegram.Bot`.
|
||||
of :class:`~telegram.Bot`. Alternatively, a ``t.me`` link to a Web App of the bot can
|
||||
be specified in the object instead of the Web App's URL, in which case the Web App
|
||||
will be opened as if the user pressed the link.
|
||||
"""
|
||||
|
||||
__slots__ = ("text", "web_app")
|
||||
|
|
|
@ -52,6 +52,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
|||
from telegram._linkpreviewoptions import LinkPreviewOptions
|
||||
from telegram._messageautodeletetimerchanged import MessageAutoDeleteTimerChanged
|
||||
from telegram._messageentity import MessageEntity
|
||||
from telegram._paidmedia import PaidMediaInfo
|
||||
from telegram._passport.passportdata import PassportData
|
||||
from telegram._payment.invoice import Invoice
|
||||
from telegram._payment.successfulpayment import SuccessfulPayment
|
||||
|
@ -380,7 +381,8 @@ class Message(MaybeInaccessibleMessage):
|
|||
.. versionchanged:: 20.0
|
||||
|sequenceclassargs|
|
||||
|
||||
caption (:obj:`str`, optional): Caption for the animation, audio, document, photo, video
|
||||
caption (:obj:`str`, optional): Caption for the animation, audio, document, paid media,
|
||||
photo, video
|
||||
or voice, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters.
|
||||
contact (:class:`telegram.Contact`, optional): Message is a shared contact, information
|
||||
about the contact.
|
||||
|
@ -571,6 +573,10 @@ class Message(MaybeInaccessibleMessage):
|
|||
background set.
|
||||
|
||||
.. versionadded:: 21.2
|
||||
paid_media (:obj:`telegram.PaidMediaInfo`, optional): Message contains paid media;
|
||||
information about the paid media.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
message_id (:obj:`int`): Unique message identifier inside this chat.
|
||||
|
@ -692,7 +698,8 @@ class Message(MaybeInaccessibleMessage):
|
|||
|
||||
.. versionchanged:: 20.0
|
||||
|tupleclassattrs|
|
||||
caption (:obj:`str`): Optional. Caption for the animation, audio, document, photo, video
|
||||
caption (:obj:`str`): Optional. Caption for the animation, audio, document, paid media,
|
||||
photo, video
|
||||
or voice, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters.
|
||||
contact (:class:`telegram.Contact`): Optional. Message is a shared contact, information
|
||||
about the contact.
|
||||
|
@ -884,6 +891,10 @@ class Message(MaybeInaccessibleMessage):
|
|||
background set
|
||||
|
||||
.. versionadded:: 21.2
|
||||
paid_media (:obj:`telegram.PaidMediaInfo`): Optional. Message contains paid media;
|
||||
information about the paid media.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. |custom_emoji_no_md1_support| replace:: Since custom emoji entities are not supported by
|
||||
:attr:`~telegram.constants.ParseMode.MARKDOWN`, this method now raises a
|
||||
|
@ -950,6 +961,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
"new_chat_members",
|
||||
"new_chat_photo",
|
||||
"new_chat_title",
|
||||
"paid_media",
|
||||
"passport_data",
|
||||
"photo",
|
||||
"pinned_message",
|
||||
|
@ -1067,6 +1079,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
chat_background_set: Optional[ChatBackground] = None,
|
||||
effect_id: Optional[str] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
paid_media: Optional[PaidMediaInfo] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -1168,6 +1181,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
self.chat_background_set: Optional[ChatBackground] = chat_background_set
|
||||
self.effect_id: Optional[str] = effect_id
|
||||
self.show_caption_above_media: Optional[bool] = show_caption_above_media
|
||||
self.paid_media: Optional[PaidMediaInfo] = paid_media
|
||||
|
||||
self._effective_attachment = DEFAULT_NONE
|
||||
|
||||
|
@ -1283,6 +1297,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
data["users_shared"] = UsersShared.de_json(data.get("users_shared"), bot)
|
||||
data["chat_shared"] = ChatShared.de_json(data.get("chat_shared"), bot)
|
||||
data["chat_background_set"] = ChatBackground.de_json(data.get("chat_background_set"), bot)
|
||||
data["paid_media"] = PaidMediaInfo.de_json(data.get("paid_media"), bot)
|
||||
|
||||
# Unfortunately, this needs to be here due to cyclic imports
|
||||
from telegram._giveaway import ( # pylint: disable=import-outside-toplevel
|
||||
|
@ -1346,6 +1361,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
Location,
|
||||
PassportData,
|
||||
Sequence[PhotoSize],
|
||||
PaidMediaInfo,
|
||||
Poll,
|
||||
Sticker,
|
||||
Story,
|
||||
|
@ -1369,6 +1385,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
* :class:`telegram.Location`
|
||||
* :class:`telegram.PassportData`
|
||||
* List[:class:`telegram.PhotoSize`]
|
||||
* :class:`telegram.PaidMediaInfo`
|
||||
* :class:`telegram.Poll`
|
||||
* :class:`telegram.Sticker`
|
||||
* :class:`telegram.Story`
|
||||
|
@ -1386,6 +1403,9 @@ class Message(MaybeInaccessibleMessage):
|
|||
:attr:`dice`, :attr:`passport_data` and :attr:`poll` are now also considered to be an
|
||||
attachment.
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
:attr:`paid_media` is now also considered to be an attachment.
|
||||
|
||||
"""
|
||||
if not isinstance(self._effective_attachment, DefaultValue):
|
||||
return self._effective_attachment
|
||||
|
|
290
telegram/_paidmedia.py
Normal file
290
telegram/_paidmedia.py
Normal file
|
@ -0,0 +1,290 @@
|
|||
#!/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 objects that represent paid media in Telegram."""
|
||||
|
||||
from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type
|
||||
|
||||
from telegram import constants
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._files.video import Video
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class PaidMedia(TelegramObject):
|
||||
"""Describes the paid media added to a message. Currently, it can be one of:
|
||||
|
||||
* :class:`telegram.PaidMediaPreview`
|
||||
* :class:`telegram.PaidMediaPhoto`
|
||||
* :class:`telegram.PaidMediaVideo`
|
||||
|
||||
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:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the paid media.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the paid media.
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
PREVIEW: Final[str] = constants.PaidMediaType.PREVIEW
|
||||
""":const:`telegram.constants.PaidMediaType.PREVIEW`"""
|
||||
PHOTO: Final[str] = constants.PaidMediaType.PHOTO
|
||||
""":const:`telegram.constants.PaidMediaType.PHOTO`"""
|
||||
VIDEO: Final[str] = constants.PaidMediaType.VIDEO
|
||||
""":const:`telegram.constants.PaidMediaType.VIDEO`"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str, # pylint: disable=redefined-builtin
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: str = enum.get_member(constants.PaidMediaType, type, type)
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMedia"]:
|
||||
"""Converts JSON data to the appropriate :class:`PaidMedia` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`, optional): 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 PaidMedia:
|
||||
return None
|
||||
|
||||
_class_mapping: Dict[str, Type[PaidMedia]] = {
|
||||
cls.PREVIEW: PaidMediaPreview,
|
||||
cls.PHOTO: PaidMediaPhoto,
|
||||
cls.VIDEO: PaidMediaVideo,
|
||||
}
|
||||
|
||||
if cls is PaidMedia 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 PaidMediaPreview(PaidMedia):
|
||||
"""The paid media isn't available before the payment.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`width`, :attr:`height`, and :attr:`duration`
|
||||
are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PREVIEW`.
|
||||
width (:obj:`int`, optional): Media width as defined by the sender.
|
||||
height (:obj:`int`, optional): Media height as defined by the sender.
|
||||
duration (:obj:`int`, optional): Duration of the media in seconds as defined by the sender.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PREVIEW`.
|
||||
width (:obj:`int`): Optional. Media width as defined by the sender.
|
||||
height (:obj:`int`): Optional. Media height as defined by the sender.
|
||||
duration (:obj:`int`): Optional. Duration of the media in seconds as defined by the sender.
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "height", "width")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
duration: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=PaidMedia.PREVIEW, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.width: Optional[int] = width
|
||||
self.height: Optional[int] = height
|
||||
self.duration: Optional[int] = duration
|
||||
|
||||
self._id_attrs = (self.type, self.width, self.height, self.duration)
|
||||
|
||||
|
||||
class PaidMediaPhoto(PaidMedia):
|
||||
"""
|
||||
The paid media is a photo.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`photo` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PHOTO`.
|
||||
photo (Sequence[:class:`telegram.PhotoSize`]): The photo.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PHOTO`.
|
||||
photo (Tuple[:class:`telegram.PhotoSize`]): The photo.
|
||||
"""
|
||||
|
||||
__slots__ = ("photo",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
photo: Sequence["PhotoSize"],
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=PaidMedia.PHOTO, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo)
|
||||
|
||||
self._id_attrs = (self.type, self.photo)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaPhoto"]:
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["photo"] = PhotoSize.de_list(data.get("photo"), bot=bot)
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class PaidMediaVideo(PaidMedia):
|
||||
"""
|
||||
The paid media is a video.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`video` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.VIDEO`.
|
||||
video (:class:`telegram.Video`): The video.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.VIDEO`.
|
||||
video (:class:`telegram.Video`): The video.
|
||||
"""
|
||||
|
||||
__slots__ = ("video",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
video: Video,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=PaidMedia.VIDEO, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.video: Video = video
|
||||
|
||||
self._id_attrs = (self.type, self.video)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaVideo"]:
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["video"] = Video.de_json(data.get("video"), bot=bot)
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class PaidMediaInfo(TelegramObject):
|
||||
"""
|
||||
Describes the paid media added to a message.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`star_count` and :attr:`paid_media` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access to
|
||||
the media.
|
||||
paid_media (Sequence[:class:`telegram.PaidMedia`]): Information about the paid media.
|
||||
|
||||
Attributes:
|
||||
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access to
|
||||
the media.
|
||||
paid_media (Tuple[:class:`telegram.PaidMedia`]): Information about the paid media.
|
||||
"""
|
||||
|
||||
__slots__ = ("paid_media", "star_count")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
star_count: int,
|
||||
paid_media: Sequence[PaidMedia],
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.star_count: int = star_count
|
||||
self.paid_media: Tuple[PaidMedia, ...] = parse_sequence_arg(paid_media)
|
||||
|
||||
self._id_attrs = (self.star_count, self.paid_media)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaInfo"]:
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
|
||||
return super().de_json(data=data, bot=bot)
|
|
@ -50,7 +50,7 @@ class PreCheckoutQuery(TelegramObject):
|
|||
`currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_,
|
||||
it shows the number of digits past the decimal point for each currency
|
||||
(2 for the majority of currencies).
|
||||
invoice_payload (:obj:`str`): Bot specified invoice payload.
|
||||
invoice_payload (:obj:`str`): Bot-specified invoice payload.
|
||||
shipping_option_id (:obj:`str`, optional): Identifier of the shipping option chosen by the
|
||||
user.
|
||||
order_info (:class:`telegram.OrderInfo`, optional): Order info provided by the user.
|
||||
|
@ -66,7 +66,7 @@ class PreCheckoutQuery(TelegramObject):
|
|||
`currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_,
|
||||
it shows the number of digits past the decimal point for each currency
|
||||
(2 for the majority of currencies).
|
||||
invoice_payload (:obj:`str`): Bot specified invoice payload.
|
||||
invoice_payload (:obj:`str`): Bot-specified invoice payload.
|
||||
shipping_option_id (:obj:`str`): Optional. Identifier of the shipping option chosen by the
|
||||
user.
|
||||
order_info (:class:`telegram.OrderInfo`): Optional. Order info provided by the user.
|
||||
|
|
|
@ -43,13 +43,13 @@ class ShippingQuery(TelegramObject):
|
|||
Args:
|
||||
id (:obj:`str`): Unique query identifier.
|
||||
from_user (:class:`telegram.User`): User who sent the query.
|
||||
invoice_payload (:obj:`str`): Bot specified invoice payload.
|
||||
invoice_payload (:obj:`str`): Bot-specified invoice payload.
|
||||
shipping_address (:class:`telegram.ShippingAddress`): User specified shipping address.
|
||||
|
||||
Attributes:
|
||||
id (:obj:`str`): Unique query identifier.
|
||||
from_user (:class:`telegram.User`): User who sent the query.
|
||||
invoice_payload (:obj:`str`): Bot specified invoice payload.
|
||||
invoice_payload (:obj:`str`): Bot-specified invoice payload.
|
||||
shipping_address (:class:`telegram.ShippingAddress`): User specified shipping address.
|
||||
|
||||
|
||||
|
|
|
@ -183,8 +183,9 @@ class TransactionPartner(TelegramObject):
|
|||
"""This object describes the source of a transaction, or its recipient for outgoing
|
||||
transactions. Currently, it can be one of:
|
||||
|
||||
* :class:`TransactionPartnerFragment`
|
||||
* :class:`TransactionPartnerUser`
|
||||
* :class:`TransactionPartnerFragment`
|
||||
* :class:`TransactionPartnerTelegramAds`
|
||||
* :class:`TransactionPartnerOther`
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
|
@ -207,6 +208,8 @@ class TransactionPartner(TelegramObject):
|
|||
""":const:`telegram.constants.TransactionPartnerType.USER`"""
|
||||
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`"""
|
||||
|
||||
def __init__(self, type: str, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
@ -242,6 +245,7 @@ class TransactionPartner(TelegramObject):
|
|||
cls.FRAGMENT: TransactionPartnerFragment,
|
||||
cls.USER: TransactionPartnerUser,
|
||||
cls.OTHER: TransactionPartnerOther,
|
||||
cls.TELEGRAM_ADS: TransactionPartnerTelegramAds,
|
||||
}
|
||||
|
||||
if cls is TransactionPartner and data.get("type") in _class_mapping:
|
||||
|
@ -305,20 +309,29 @@ class TransactionPartnerUser(TransactionPartner):
|
|||
|
||||
Args:
|
||||
user (:class:`telegram.User`): Information about the user.
|
||||
invoice_payload (:obj:`str`, optional): Bot-specified invoice payload.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.USER`.
|
||||
user (:class:`telegram.User`): Information about the user.
|
||||
invoice_payload (:obj:`str`): Optional. Bot-specified invoice payload.
|
||||
"""
|
||||
|
||||
__slots__ = ("user",)
|
||||
__slots__ = ("invoice_payload", "user")
|
||||
|
||||
def __init__(self, user: "User", *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
user: "User",
|
||||
invoice_payload: Optional[str] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.USER, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.user: User = user
|
||||
self.invoice_payload: Optional[str] = invoice_payload
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.user,
|
||||
|
@ -355,6 +368,23 @@ class TransactionPartnerOther(TransactionPartner):
|
|||
self._freeze()
|
||||
|
||||
|
||||
class TransactionPartnerTelegramAds(TransactionPartner):
|
||||
"""Describes a withdrawal transaction to the Telegram Ads platform.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
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 StarTransaction(TelegramObject):
|
||||
"""Describes a Telegram Star transaction.
|
||||
|
|
@ -44,7 +44,7 @@ class SuccessfulPayment(TelegramObject):
|
|||
`currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_,
|
||||
it shows the number of digits past the decimal point for each currency
|
||||
(2 for the majority of currencies).
|
||||
invoice_payload (:obj:`str`): Bot specified invoice payload.
|
||||
invoice_payload (:obj:`str`): Bot-specified invoice payload.
|
||||
shipping_option_id (:obj:`str`, optional): Identifier of the shipping option chosen by the
|
||||
user.
|
||||
order_info (:class:`telegram.OrderInfo`, optional): Order info provided by the user.
|
||||
|
@ -60,7 +60,7 @@ class SuccessfulPayment(TelegramObject):
|
|||
`currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_,
|
||||
it shows the number of digits past the decimal point for each currency
|
||||
(2 for the majority of currencies).
|
||||
invoice_payload (:obj:`str`): Bot specified invoice payload.
|
||||
invoice_payload (:obj:`str`): Bot-specified invoice payload.
|
||||
shipping_option_id (:obj:`str`): Optional. Identifier of the shipping option chosen by the
|
||||
user.
|
||||
order_info (:class:`telegram.OrderInfo`): Optional. Order info provided by the user.
|
||||
|
|
|
@ -38,7 +38,7 @@ if TYPE_CHECKING:
|
|||
|
||||
class InputPollOption(TelegramObject):
|
||||
"""
|
||||
This object contains information about one answer option in a poll to send.
|
||||
This object contains information about one answer option in a poll to be sent.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`text` is equal.
|
||||
|
|
|
@ -37,6 +37,7 @@ from telegram._giveaway import Giveaway, GiveawayWinners
|
|||
from telegram._linkpreviewoptions import LinkPreviewOptions
|
||||
from telegram._messageentity import MessageEntity
|
||||
from telegram._messageorigin import MessageOrigin
|
||||
from telegram._paidmedia import PaidMediaInfo
|
||||
from telegram._payment.invoice import Invoice
|
||||
from telegram._poll import Poll
|
||||
from telegram._story import Story
|
||||
|
@ -101,6 +102,10 @@ class ExternalReplyInfo(TelegramObject):
|
|||
poll (:class:`telegram.Poll`, optional): Message is a native poll, information about the
|
||||
poll.
|
||||
venue (:class:`telegram.Venue`, optional): Message is a venue, information about the venue.
|
||||
paid_media (:class:`telegram.PaidMedia`, optional): Message contains paid media;
|
||||
information about the paid media.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
origin (:class:`telegram.MessageOrigin`): Origin of the message replied to by the given
|
||||
|
@ -144,6 +149,10 @@ class ExternalReplyInfo(TelegramObject):
|
|||
poll (:class:`telegram.Poll`): Optional. Message is a native poll, information about the
|
||||
poll.
|
||||
venue (:class:`telegram.Venue`): Optional. Message is a venue, information about the venue.
|
||||
paid_media (:class:`telegram.PaidMedia`): Optional. Message contains paid media;
|
||||
information about the paid media.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
|
@ -162,6 +171,7 @@ class ExternalReplyInfo(TelegramObject):
|
|||
"location",
|
||||
"message_id",
|
||||
"origin",
|
||||
"paid_media",
|
||||
"photo",
|
||||
"poll",
|
||||
"sticker",
|
||||
|
@ -197,6 +207,7 @@ class ExternalReplyInfo(TelegramObject):
|
|||
location: Optional[Location] = None,
|
||||
poll: Optional[Poll] = None,
|
||||
venue: Optional[Venue] = None,
|
||||
paid_media: Optional[PaidMediaInfo] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -225,6 +236,7 @@ class ExternalReplyInfo(TelegramObject):
|
|||
self.location: Optional[Location] = location
|
||||
self.poll: Optional[Poll] = poll
|
||||
self.venue: Optional[Venue] = venue
|
||||
self.paid_media: Optional[PaidMediaInfo] = paid_media
|
||||
|
||||
self._id_attrs = (self.origin,)
|
||||
|
||||
|
@ -263,6 +275,7 @@ class ExternalReplyInfo(TelegramObject):
|
|||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["poll"] = Poll.de_json(data.get("poll"), bot)
|
||||
data["venue"] = Venue.de_json(data.get("venue"), bot)
|
||||
data["paid_media"] = PaidMediaInfo.de_json(data.get("paid_media"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ __all__ = [
|
|||
"InlineQueryResultType",
|
||||
"InlineQueryResultsButtonLimit",
|
||||
"InputMediaType",
|
||||
"InputPaidMediaType",
|
||||
"InvoiceLimit",
|
||||
"KeyboardButtonRequestUsersLimit",
|
||||
"LocationLimit",
|
||||
|
@ -82,6 +83,7 @@ __all__ = [
|
|||
"MessageLimit",
|
||||
"MessageOriginType",
|
||||
"MessageType",
|
||||
"PaidMediaType",
|
||||
"ParseMode",
|
||||
"PollLimit",
|
||||
"PollType",
|
||||
|
@ -149,7 +151,7 @@ class _AccentColor(NamedTuple):
|
|||
#: :data:`telegram.__bot_api_version_info__`.
|
||||
#:
|
||||
#: .. versionadded:: 20.0
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=5)
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=6)
|
||||
#: :obj:`str`: Telegram Bot API
|
||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||
#: :data:`telegram.__bot_api_version__`.
|
||||
|
@ -1259,6 +1261,21 @@ class InputMediaType(StringEnum):
|
|||
""":obj:`str`: Type of :class:`telegram.InputMediaVideo`."""
|
||||
|
||||
|
||||
class InputPaidMediaType(StringEnum):
|
||||
"""This enum contains the available types of :class:`telegram.InputPaidMedia`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
PHOTO = "photo"
|
||||
""":obj:`str`: Type of :class:`telegram.InputMediaPhoto`."""
|
||||
VIDEO = "video"
|
||||
""":obj:`str`: Type of :class:`telegram.InputMediaVideo`."""
|
||||
|
||||
|
||||
class InlineQueryLimit(IntEnum):
|
||||
"""This enum contains limitations for :class:`telegram.InlineQuery`/
|
||||
:meth:`telegram.Bot.answer_inline_query`. The enum members of this enumeration are instances
|
||||
|
@ -1602,6 +1619,11 @@ class MessageAttachmentType(StringEnum):
|
|||
""":obj:`str`: Messages with :attr:`telegram.Message.invoice`."""
|
||||
LOCATION = "location"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.location`."""
|
||||
PAID_MEDIA = "paid_media"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.paid_media`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
PASSPORT_DATA = "passport_data"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.passport_data`."""
|
||||
PHOTO = "photo"
|
||||
|
@ -1883,6 +1905,11 @@ class MessageType(StringEnum):
|
|||
""":obj:`str`: Messages with :attr:`telegram.Message.new_chat_title`."""
|
||||
NEW_CHAT_PHOTO = "new_chat_photo"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.new_chat_photo`."""
|
||||
PAID_MEDIA = "paid_media"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.paid_media`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
PASSPORT_DATA = "passport_data"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.passport_data`."""
|
||||
PHOTO = "photo"
|
||||
|
@ -1951,6 +1978,24 @@ class MessageType(StringEnum):
|
|||
"""
|
||||
|
||||
|
||||
class PaidMediaType(StringEnum):
|
||||
"""
|
||||
This enum contains the available types of :class:`telegram.PaidMedia`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
PREVIEW = "preview"
|
||||
""":obj:`str`: The type of :class:`telegram.PaidMediaPreview`."""
|
||||
VIDEO = "video"
|
||||
""":obj:`str`: The type of :class:`telegram.PaidMediaVideo`."""
|
||||
PHOTO = "photo"
|
||||
""":obj:`str`: The type of :class:`telegram.PaidMediaPhoto`."""
|
||||
|
||||
|
||||
class PollingLimit(IntEnum):
|
||||
"""This enum contains limitations for :paramref:`telegram.Bot.get_updates.limit`.
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
@ -2490,6 +2535,8 @@ class TransactionPartnerType(StringEnum):
|
|||
""":obj:`str`: Transaction with a user."""
|
||||
OTHER = "other"
|
||||
""":obj:`str`: Transaction with unknown source or recipient."""
|
||||
TELEGRAM_ADS = "telegram_ads"
|
||||
""":obj:`str`: Transaction with Telegram Ads."""
|
||||
|
||||
|
||||
class ParseMode(StringEnum):
|
||||
|
|
|
@ -107,6 +107,7 @@ if TYPE_CHECKING:
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputPaidMedia,
|
||||
InputSticker,
|
||||
LabeledPrice,
|
||||
MessageEntity,
|
||||
|
@ -4216,6 +4217,50 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
async def send_paid_media(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
star_count: int,
|
||||
media: Sequence["InputPaidMedia"],
|
||||
caption: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
rate_limit_args: Optional[RLARGS] = None,
|
||||
) -> Message:
|
||||
return await super().send_paid_media(
|
||||
chat_id=chat_id,
|
||||
star_count=star_count,
|
||||
media=media,
|
||||
caption=caption,
|
||||
parse_mode=parse_mode,
|
||||
caption_entities=caption_entities,
|
||||
show_caption_above_media=show_caption_above_media,
|
||||
disable_notification=disable_notification,
|
||||
protect_content=protect_content,
|
||||
reply_parameters=reply_parameters,
|
||||
reply_markup=reply_markup,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
)
|
||||
|
||||
# updated camelCase aliases
|
||||
getMe = get_me
|
||||
sendMessage = send_message
|
||||
|
@ -4339,3 +4384,4 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
replaceStickerInSet = replace_sticker_in_set
|
||||
refundStarPayment = refund_star_payment
|
||||
getStarTransactions = get_star_transactions
|
||||
sendPaidMedia = send_paid_media
|
||||
|
|
|
@ -23,7 +23,7 @@ from datetime import datetime
|
|||
from typing import List, Optional, Sequence, Tuple, final
|
||||
|
||||
from telegram._files.inputfile import InputFile
|
||||
from telegram._files.inputmedia import InputMedia
|
||||
from telegram._files.inputmedia import InputMedia, InputPaidMedia
|
||||
from telegram._files.inputsticker import InputSticker
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.datetime import to_timestamp
|
||||
|
@ -117,7 +117,7 @@ class RequestParameter:
|
|||
return value.attach_uri, [value]
|
||||
return None, [value]
|
||||
|
||||
if isinstance(value, InputMedia) and isinstance(value.media, InputFile):
|
||||
if isinstance(value, (InputMedia, InputPaidMedia)) and isinstance(value.media, InputFile):
|
||||
# We call to_dict and change the returned dict instead of overriding
|
||||
# value.media in case the same value is reused for another request
|
||||
data = value.to_dict()
|
||||
|
|
|
@ -31,6 +31,8 @@ from telegram import (
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputPaidMediaPhoto,
|
||||
InputPaidMediaVideo,
|
||||
Message,
|
||||
MessageEntity,
|
||||
ReplyParameters,
|
||||
|
@ -134,6 +136,25 @@ def input_media_document(class_thumb_file):
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def input_paid_media_photo():
|
||||
return InputPaidMediaPhoto(
|
||||
media=TestInputMediaPhotoBase.media,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def input_paid_media_video(class_thumb_file):
|
||||
return InputPaidMediaVideo(
|
||||
media=TestInputMediaVideoBase.media,
|
||||
thumbnail=class_thumb_file,
|
||||
width=TestInputMediaVideoBase.width,
|
||||
height=TestInputMediaVideoBase.height,
|
||||
duration=TestInputMediaVideoBase.duration,
|
||||
supports_streaming=TestInputMediaVideoBase.supports_streaming,
|
||||
)
|
||||
|
||||
|
||||
class TestInputMediaVideoBase:
|
||||
type_ = "video"
|
||||
media = "NOTAREALFILEID"
|
||||
|
@ -514,6 +535,91 @@ class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
|
|||
assert input_media_document.thumbnail == data_file("telegram.jpg").as_uri()
|
||||
|
||||
|
||||
class TestInputPaidMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
|
||||
def test_slot_behaviour(self, input_paid_media_photo):
|
||||
inst = input_paid_media_photo
|
||||
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_expected_values(self, input_paid_media_photo):
|
||||
assert input_paid_media_photo.type == self.type_
|
||||
assert input_paid_media_photo.media == self.media
|
||||
|
||||
def test_to_dict(self, input_paid_media_photo):
|
||||
input_paid_media_photo_dict = input_paid_media_photo.to_dict()
|
||||
assert input_paid_media_photo_dict["type"] == input_paid_media_photo.type
|
||||
assert input_paid_media_photo_dict["media"] == input_paid_media_photo.media
|
||||
|
||||
def test_with_photo(self, photo): # noqa: F811
|
||||
# fixture found in test_photo
|
||||
input_paid_media_photo = InputPaidMediaPhoto(photo)
|
||||
assert input_paid_media_photo.type == self.type_
|
||||
assert input_paid_media_photo.media == photo.file_id
|
||||
|
||||
def test_with_photo_file(self, photo_file): # noqa: F811
|
||||
# fixture found in test_photo
|
||||
input_paid_media_photo = InputPaidMediaPhoto(photo_file)
|
||||
assert input_paid_media_photo.type == self.type_
|
||||
assert isinstance(input_paid_media_photo.media, InputFile)
|
||||
|
||||
def test_with_local_files(self):
|
||||
input_paid_media_photo = InputPaidMediaPhoto(data_file("telegram.jpg"))
|
||||
assert input_paid_media_photo.media == data_file("telegram.jpg").as_uri()
|
||||
|
||||
|
||||
class TestInputPaidMediaVideoWithoutRequest(TestInputMediaVideoBase):
|
||||
def test_slot_behaviour(self, input_paid_media_video):
|
||||
inst = input_paid_media_video
|
||||
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_expected_values(self, input_paid_media_video):
|
||||
assert input_paid_media_video.type == self.type_
|
||||
assert input_paid_media_video.media == self.media
|
||||
assert input_paid_media_video.width == self.width
|
||||
assert input_paid_media_video.height == self.height
|
||||
assert input_paid_media_video.duration == self.duration
|
||||
assert input_paid_media_video.supports_streaming == self.supports_streaming
|
||||
assert isinstance(input_paid_media_video.thumbnail, InputFile)
|
||||
|
||||
def test_to_dict(self, input_paid_media_video):
|
||||
input_paid_media_video_dict = input_paid_media_video.to_dict()
|
||||
assert input_paid_media_video_dict["type"] == input_paid_media_video.type
|
||||
assert input_paid_media_video_dict["media"] == input_paid_media_video.media
|
||||
assert input_paid_media_video_dict["width"] == input_paid_media_video.width
|
||||
assert input_paid_media_video_dict["height"] == input_paid_media_video.height
|
||||
assert input_paid_media_video_dict["duration"] == input_paid_media_video.duration
|
||||
assert (
|
||||
input_paid_media_video_dict["supports_streaming"]
|
||||
== input_paid_media_video.supports_streaming
|
||||
)
|
||||
assert input_paid_media_video_dict["thumbnail"] == input_paid_media_video.thumbnail
|
||||
|
||||
def test_with_video(self, video): # noqa: F811
|
||||
# fixture found in test_video
|
||||
input_paid_media_video = InputPaidMediaVideo(video)
|
||||
assert input_paid_media_video.type == self.type_
|
||||
assert input_paid_media_video.media == video.file_id
|
||||
assert input_paid_media_video.width == video.width
|
||||
assert input_paid_media_video.height == video.height
|
||||
assert input_paid_media_video.duration == video.duration
|
||||
|
||||
def test_with_video_file(self, video_file): # noqa: F811
|
||||
# fixture found in test_video
|
||||
input_paid_media_video = InputPaidMediaVideo(video_file)
|
||||
assert input_paid_media_video.type == self.type_
|
||||
assert isinstance(input_paid_media_video.media, InputFile)
|
||||
|
||||
def test_with_local_files(self):
|
||||
input_paid_media_video = InputPaidMediaVideo(
|
||||
data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg")
|
||||
)
|
||||
assert input_paid_media_video.media == data_file("telegram.mp4").as_uri()
|
||||
assert input_paid_media_video.thumbnail == data_file("telegram.jpg").as_uri()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def media_group(photo, thumb): # noqa: F811
|
||||
return [
|
||||
|
@ -1044,3 +1150,20 @@ class TestSendMediaGroupWithRequest:
|
|||
assert message.caption_entities == ()
|
||||
# make sure that the media was not modified
|
||||
assert media.parse_mode == copied_media.parse_mode
|
||||
|
||||
async def test_send_paid_media(self, bot, channel_id, photo_file, video_file): # noqa: F811
|
||||
msg = await bot.send_paid_media(
|
||||
chat_id=channel_id,
|
||||
star_count=20,
|
||||
media=[
|
||||
InputPaidMediaPhoto(media=photo_file),
|
||||
InputPaidMediaVideo(media=video_file),
|
||||
],
|
||||
caption="bye onlyfans",
|
||||
show_caption_above_media=True,
|
||||
)
|
||||
|
||||
assert isinstance(msg, Message)
|
||||
assert msg.caption == "bye onlyfans"
|
||||
assert msg.show_caption_above_media
|
||||
assert msg.paid_media.star_count == 20
|
||||
|
|
|
@ -1244,6 +1244,22 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
123, [ReactionTypeEmoji(ReactionEmoji.THUMBS_DOWN)], True
|
||||
)
|
||||
|
||||
async def test_instance_method_send_paid_media(self, monkeypatch, chat):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
return (
|
||||
kwargs["chat_id"] == chat.id
|
||||
and kwargs["media"] == "media"
|
||||
and kwargs["star_count"] == 42
|
||||
and kwargs["caption"] == "stars"
|
||||
)
|
||||
|
||||
assert check_shortcut_signature(Chat.send_paid_media, Bot.send_paid_media, ["chat_id"], [])
|
||||
assert await check_shortcut_call(chat.send_paid_media, chat.get_bot(), "send_paid_media")
|
||||
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")
|
||||
|
||||
def test_mention_html(self):
|
||||
chat = Chat(id=1, type="foo")
|
||||
with pytest.raises(TypeError, match="Can not create a mention to a private group chat"):
|
||||
|
|
|
@ -55,13 +55,15 @@ def chat_full_info(bot):
|
|||
bio=TestChatFullInfoBase.bio,
|
||||
linked_chat_id=TestChatFullInfoBase.linked_chat_id,
|
||||
location=TestChatFullInfoBase.location,
|
||||
has_private_forwards=True,
|
||||
has_protected_content=True,
|
||||
has_visible_history=True,
|
||||
join_to_send_messages=True,
|
||||
join_by_request=True,
|
||||
has_restricted_voice_and_video_messages=True,
|
||||
is_forum=True,
|
||||
has_private_forwards=TestChatFullInfoBase.has_private_forwards,
|
||||
has_protected_content=TestChatFullInfoBase.has_protected_content,
|
||||
has_visible_history=TestChatFullInfoBase.has_visible_history,
|
||||
join_to_send_messages=TestChatFullInfoBase.join_to_send_messages,
|
||||
join_by_request=TestChatFullInfoBase.join_by_request,
|
||||
has_restricted_voice_and_video_messages=(
|
||||
TestChatFullInfoBase.has_restricted_voice_and_video_messages
|
||||
),
|
||||
is_forum=TestChatFullInfoBase.is_forum,
|
||||
active_usernames=TestChatFullInfoBase.active_usernames,
|
||||
emoji_status_custom_emoji_id=TestChatFullInfoBase.emoji_status_custom_emoji_id,
|
||||
emoji_status_expiration_date=TestChatFullInfoBase.emoji_status_expiration_date,
|
||||
|
@ -76,10 +78,11 @@ def chat_full_info(bot):
|
|||
business_intro=TestChatFullInfoBase.business_intro,
|
||||
business_location=TestChatFullInfoBase.business_location,
|
||||
business_opening_hours=TestChatFullInfoBase.business_opening_hours,
|
||||
birthdate=Birthdate(1, 1),
|
||||
birthdate=TestChatFullInfoBase.birthdate,
|
||||
personal_chat=TestChatFullInfoBase.personal_chat,
|
||||
first_name="first_name",
|
||||
last_name="last_name",
|
||||
first_name=TestChatFullInfoBase.first_name,
|
||||
last_name=TestChatFullInfoBase.last_name,
|
||||
can_send_paid_media=TestChatFullInfoBase.can_send_paid_media,
|
||||
)
|
||||
chat.set_bot(bot)
|
||||
chat._unfreeze()
|
||||
|
@ -136,6 +139,7 @@ class TestChatFullInfoBase:
|
|||
personal_chat = Chat(3, "private", "private")
|
||||
first_name = "first_name"
|
||||
last_name = "last_name"
|
||||
can_send_paid_media = True
|
||||
|
||||
|
||||
class TestChatFullInfoWithoutRequest(TestChatFullInfoBase):
|
||||
|
@ -188,6 +192,7 @@ class TestChatFullInfoWithoutRequest(TestChatFullInfoBase):
|
|||
"personal_chat": self.personal_chat.to_dict(),
|
||||
"first_name": self.first_name,
|
||||
"last_name": self.last_name,
|
||||
"can_send_paid_media": self.can_send_paid_media,
|
||||
}
|
||||
cfi = ChatFullInfo.de_json(json_dict, bot)
|
||||
assert cfi.id == self.id_
|
||||
|
@ -232,6 +237,7 @@ class TestChatFullInfoWithoutRequest(TestChatFullInfoBase):
|
|||
assert cfi.first_name == self.first_name
|
||||
assert cfi.last_name == self.last_name
|
||||
assert cfi.max_reaction_count == self.max_reaction_count
|
||||
assert cfi.can_send_paid_media == self.can_send_paid_media
|
||||
|
||||
def test_de_json_localization(self, bot, raw_bot, tz_bot):
|
||||
json_dict = {
|
||||
|
@ -305,6 +311,7 @@ class TestChatFullInfoWithoutRequest(TestChatFullInfoBase):
|
|||
assert cfi_dict["personal_chat"] == cfi.personal_chat.to_dict()
|
||||
assert cfi_dict["first_name"] == cfi.first_name
|
||||
assert cfi_dict["last_name"] == cfi.last_name
|
||||
assert cfi_dict["can_send_paid_media"] == cfi.can_send_paid_media
|
||||
|
||||
assert cfi_dict["max_reaction_count"] == cfi.max_reaction_count
|
||||
|
||||
|
|
|
@ -216,6 +216,8 @@ class TestConstantsWithoutRequest:
|
|||
name = to_snake_case(match.group(1))
|
||||
if name == "photo_size":
|
||||
name = "photo"
|
||||
if name == "paid_media_info":
|
||||
name = "paid_media"
|
||||
try:
|
||||
constants.MessageAttachmentType(name)
|
||||
except ValueError:
|
||||
|
|
|
@ -46,6 +46,8 @@ from telegram import (
|
|||
MessageAutoDeleteTimerChanged,
|
||||
MessageEntity,
|
||||
MessageOriginChat,
|
||||
PaidMediaInfo,
|
||||
PaidMediaPreview,
|
||||
PassportData,
|
||||
PhotoSize,
|
||||
Poll,
|
||||
|
@ -275,6 +277,7 @@ def message(bot):
|
|||
{"chat_background_set": ChatBackground(type=BackgroundTypeChatTheme("ice"))},
|
||||
{"effect_id": "123456789"},
|
||||
{"show_caption_above_media": True},
|
||||
{"paid_media": PaidMediaInfo(5, [PaidMediaPreview(10, 10, 10)])},
|
||||
],
|
||||
ids=[
|
||||
"reply",
|
||||
|
@ -346,6 +349,7 @@ def message(bot):
|
|||
"chat_background_set",
|
||||
"effect_id",
|
||||
"show_caption_above_media",
|
||||
"paid_media",
|
||||
],
|
||||
)
|
||||
def message_params(bot, request):
|
||||
|
@ -1221,6 +1225,7 @@ class TestMessageWithoutRequest(TestMessageBase):
|
|||
"game",
|
||||
"invoice",
|
||||
"location",
|
||||
"paid_media",
|
||||
"passport_data",
|
||||
"photo",
|
||||
"poll",
|
||||
|
|
|
@ -127,6 +127,8 @@ PTB_EXTRA_PARAMS = {
|
|||
"InputTextMessageContent": {"disable_web_page_preview"}, # convenience arg, here for bw compat
|
||||
"RevenueWithdrawalState": {"type"}, # attributes common to all subclasses
|
||||
"TransactionPartner": {"type"}, # attributes common to all subclasses
|
||||
"PaidMedia": {"type"}, # attributes common to all subclasses
|
||||
"InputPaidMedia": {"type", "media"}, # attributes common to all subclasses
|
||||
}
|
||||
|
||||
|
||||
|
@ -153,6 +155,8 @@ PTB_IGNORED_PARAMS = {
|
|||
r"BackgroundFill\w+": {"type"},
|
||||
r"RevenueWithdrawalState\w+": {"type"},
|
||||
r"TransactionPartner\w+": {"type"},
|
||||
r"PaidMedia\w+": {"type"},
|
||||
r"InputPaidMedia\w+": {"type"},
|
||||
}
|
||||
|
||||
|
||||
|
|
325
tests/test_paidmedia.py
Normal file
325
tests/test_paidmedia.py
Normal file
|
@ -0,0 +1,325 @@
|
|||
#!/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/].
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
Dice,
|
||||
PaidMedia,
|
||||
PaidMediaInfo,
|
||||
PaidMediaPhoto,
|
||||
PaidMediaPreview,
|
||||
PaidMediaVideo,
|
||||
PhotoSize,
|
||||
Video,
|
||||
)
|
||||
from telegram.constants import PaidMediaType
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
PaidMedia.PREVIEW,
|
||||
PaidMedia.PHOTO,
|
||||
PaidMedia.VIDEO,
|
||||
],
|
||||
)
|
||||
def pm_scope_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
PaidMediaPreview,
|
||||
PaidMediaPhoto,
|
||||
PaidMediaVideo,
|
||||
],
|
||||
ids=[
|
||||
PaidMedia.PREVIEW,
|
||||
PaidMedia.PHOTO,
|
||||
PaidMedia.VIDEO,
|
||||
],
|
||||
)
|
||||
def pm_scope_class(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=[
|
||||
(
|
||||
PaidMediaPreview,
|
||||
PaidMedia.PREVIEW,
|
||||
),
|
||||
(
|
||||
PaidMediaPhoto,
|
||||
PaidMedia.PHOTO,
|
||||
),
|
||||
(
|
||||
PaidMediaVideo,
|
||||
PaidMedia.VIDEO,
|
||||
),
|
||||
],
|
||||
ids=[
|
||||
PaidMedia.PREVIEW,
|
||||
PaidMedia.PHOTO,
|
||||
PaidMedia.VIDEO,
|
||||
],
|
||||
)
|
||||
def pm_scope_class_and_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def paid_media(pm_scope_class_and_type):
|
||||
# We use de_json here so that we don't have to worry about which class gets which arguments
|
||||
return pm_scope_class_and_type[0].de_json(
|
||||
{
|
||||
"type": pm_scope_class_and_type[1],
|
||||
"width": TestPaidMediaBase.width,
|
||||
"height": TestPaidMediaBase.height,
|
||||
"duration": TestPaidMediaBase.duration,
|
||||
"video": TestPaidMediaBase.video.to_dict(),
|
||||
"photo": [p.to_dict() for p in TestPaidMediaBase.photo],
|
||||
},
|
||||
bot=None,
|
||||
)
|
||||
|
||||
|
||||
def paid_media_video():
|
||||
return PaidMediaVideo(video=TestPaidMediaBase.video)
|
||||
|
||||
|
||||
def paid_media_photo():
|
||||
return PaidMediaPhoto(photo=TestPaidMediaBase.photo)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def paid_media_info():
|
||||
return PaidMediaInfo(
|
||||
star_count=TestPaidMediaInfoBase.star_count,
|
||||
paid_media=[paid_media_video(), paid_media_photo()],
|
||||
)
|
||||
|
||||
|
||||
class TestPaidMediaBase:
|
||||
width = 640
|
||||
height = 480
|
||||
duration = 60
|
||||
video = Video(
|
||||
file_id="video_file_id",
|
||||
width=640,
|
||||
height=480,
|
||||
file_unique_id="file_unique_id",
|
||||
duration=60,
|
||||
)
|
||||
photo = (
|
||||
PhotoSize(
|
||||
file_id="photo_file_id",
|
||||
width=640,
|
||||
height=480,
|
||||
file_unique_id="file_unique_id",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TestPaidMediaWithoutRequest(TestPaidMediaBase):
|
||||
def test_slot_behaviour(self, paid_media):
|
||||
inst = paid_media
|
||||
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, pm_scope_class_and_type):
|
||||
cls = pm_scope_class_and_type[0]
|
||||
type_ = pm_scope_class_and_type[1]
|
||||
|
||||
json_dict = {
|
||||
"type": type_,
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"duration": self.duration,
|
||||
"video": self.video.to_dict(),
|
||||
"photo": [p.to_dict() for p in self.photo],
|
||||
}
|
||||
pm = PaidMedia.de_json(json_dict, bot)
|
||||
assert set(pm.api_kwargs.keys()) == {
|
||||
"width",
|
||||
"height",
|
||||
"duration",
|
||||
"video",
|
||||
"photo",
|
||||
} - set(cls.__slots__)
|
||||
|
||||
assert isinstance(pm, PaidMedia)
|
||||
assert type(pm) is cls
|
||||
assert pm.type == type_
|
||||
if "width" in cls.__slots__:
|
||||
assert pm.width == self.width
|
||||
assert pm.height == self.height
|
||||
assert pm.duration == self.duration
|
||||
if "video" in cls.__slots__:
|
||||
assert pm.video == self.video
|
||||
if "photo" in cls.__slots__:
|
||||
assert pm.photo == self.photo
|
||||
|
||||
assert cls.de_json(None, bot) is None
|
||||
assert PaidMedia.de_json({}, bot) is None
|
||||
|
||||
def test_de_json_invalid_type(self, bot):
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"duration": self.duration,
|
||||
"video": self.video.to_dict(),
|
||||
"photo": [p.to_dict() for p in self.photo],
|
||||
}
|
||||
pm = PaidMedia.de_json(json_dict, bot)
|
||||
assert pm.api_kwargs == {
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"duration": self.duration,
|
||||
"video": self.video.to_dict(),
|
||||
"photo": [p.to_dict() for p in self.photo],
|
||||
}
|
||||
|
||||
assert type(pm) is PaidMedia
|
||||
assert pm.type == "invalid"
|
||||
|
||||
def test_de_json_subclass(self, pm_scope_class, bot):
|
||||
"""This makes sure that e.g. PaidMediaPreivew(data) never returns a
|
||||
TransactionPartnerPhoto instance."""
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"width": self.width,
|
||||
"height": self.height,
|
||||
"duration": self.duration,
|
||||
"video": self.video.to_dict(),
|
||||
"photo": [p.to_dict() for p in self.photo],
|
||||
}
|
||||
assert type(pm_scope_class.de_json(json_dict, bot)) is pm_scope_class
|
||||
|
||||
def test_to_dict(self, paid_media):
|
||||
pm_dict = paid_media.to_dict()
|
||||
|
||||
assert isinstance(pm_dict, dict)
|
||||
assert pm_dict["type"] == paid_media.type
|
||||
if hasattr(paid_media_info, "width"):
|
||||
assert pm_dict["width"] == paid_media.width
|
||||
assert pm_dict["height"] == paid_media.height
|
||||
assert pm_dict["duration"] == paid_media.duration
|
||||
if hasattr(paid_media_info, "video"):
|
||||
assert pm_dict["video"] == paid_media.video.to_dict()
|
||||
if hasattr(paid_media_info, "photo"):
|
||||
assert pm_dict["photo"] == [p.to_dict() for p in paid_media.photo]
|
||||
|
||||
def test_type_enum_conversion(self):
|
||||
assert type(PaidMedia("video").type) is PaidMediaType
|
||||
assert PaidMedia("unknown").type == "unknown"
|
||||
|
||||
def test_equality(self, paid_media, bot):
|
||||
a = PaidMedia("base_type")
|
||||
b = PaidMedia("base_type")
|
||||
c = paid_media
|
||||
d = deepcopy(paid_media)
|
||||
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, "video"):
|
||||
json_dict = c.to_dict()
|
||||
json_dict["video"] = Video("different", "d2", 1, 1, 1).to_dict()
|
||||
f = c.__class__.de_json(json_dict, bot)
|
||||
|
||||
assert c != f
|
||||
assert hash(c) != hash(f)
|
||||
|
||||
if hasattr(c, "photo"):
|
||||
json_dict = c.to_dict()
|
||||
json_dict["photo"] = [PhotoSize("different", "d2", 1, 1, 1).to_dict()]
|
||||
f = c.__class__.de_json(json_dict, bot)
|
||||
|
||||
assert c != f
|
||||
assert hash(c) != hash(f)
|
||||
|
||||
|
||||
class TestPaidMediaInfoBase:
|
||||
star_count = 200
|
||||
paid_media = [paid_media_video(), paid_media_photo()]
|
||||
|
||||
|
||||
class TestPaidMediaInfoWithoutRequest(TestPaidMediaInfoBase):
|
||||
def test_slot_behaviour(self, paid_media_info):
|
||||
inst = paid_media_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, bot):
|
||||
json_dict = {
|
||||
"star_count": self.star_count,
|
||||
"paid_media": [t.to_dict() for t in self.paid_media],
|
||||
}
|
||||
pmi = PaidMediaInfo.de_json(json_dict, bot)
|
||||
pmi_none = PaidMediaInfo.de_json(None, bot)
|
||||
assert pmi.paid_media == tuple(self.paid_media)
|
||||
assert pmi.star_count == self.star_count
|
||||
assert pmi_none is None
|
||||
|
||||
def test_to_dict(self, paid_media_info):
|
||||
assert paid_media_info.to_dict() == {
|
||||
"star_count": self.star_count,
|
||||
"paid_media": [t.to_dict() for t in self.paid_media],
|
||||
}
|
||||
|
||||
def test_equality(self):
|
||||
pmi1 = PaidMediaInfo(
|
||||
star_count=self.star_count, paid_media=[paid_media_video(), paid_media_photo()]
|
||||
)
|
||||
pmi2 = PaidMediaInfo(
|
||||
star_count=self.star_count, paid_media=[paid_media_video(), paid_media_photo()]
|
||||
)
|
||||
pmi3 = PaidMediaInfo(star_count=100, paid_media=[paid_media_photo()])
|
||||
|
||||
assert pmi1 == pmi2
|
||||
assert hash(pmi1) == hash(pmi2)
|
||||
|
||||
assert pmi1 != pmi3
|
||||
assert hash(pmi1) != hash(pmi3)
|
|
@ -29,6 +29,8 @@ from telegram import (
|
|||
LinkPreviewOptions,
|
||||
MessageEntity,
|
||||
MessageOriginUser,
|
||||
PaidMediaInfo,
|
||||
PaidMediaPreview,
|
||||
ReplyParameters,
|
||||
TextQuote,
|
||||
User,
|
||||
|
@ -44,6 +46,7 @@ def external_reply_info():
|
|||
message_id=TestExternalReplyInfoBase.message_id,
|
||||
link_preview_options=TestExternalReplyInfoBase.link_preview_options,
|
||||
giveaway=TestExternalReplyInfoBase.giveaway,
|
||||
paid_media=TestExternalReplyInfoBase.paid_media,
|
||||
)
|
||||
|
||||
|
||||
|
@ -59,6 +62,7 @@ class TestExternalReplyInfoBase:
|
|||
dtm.datetime.now(dtm.timezone.utc).replace(microsecond=0),
|
||||
1,
|
||||
)
|
||||
paid_media = PaidMediaInfo(5, [PaidMediaPreview(10, 10, 10)])
|
||||
|
||||
|
||||
class TestExternalReplyInfoWithoutRequest(TestExternalReplyInfoBase):
|
||||
|
@ -76,6 +80,7 @@ class TestExternalReplyInfoWithoutRequest(TestExternalReplyInfoBase):
|
|||
"message_id": self.message_id,
|
||||
"link_preview_options": self.link_preview_options.to_dict(),
|
||||
"giveaway": self.giveaway.to_dict(),
|
||||
"paid_media": self.paid_media.to_dict(),
|
||||
}
|
||||
|
||||
external_reply_info = ExternalReplyInfo.de_json(json_dict, bot)
|
||||
|
@ -86,6 +91,7 @@ class TestExternalReplyInfoWithoutRequest(TestExternalReplyInfoBase):
|
|||
assert external_reply_info.message_id == self.message_id
|
||||
assert external_reply_info.link_preview_options == self.link_preview_options
|
||||
assert external_reply_info.giveaway == self.giveaway
|
||||
assert external_reply_info.paid_media == self.paid_media
|
||||
|
||||
assert ExternalReplyInfo.de_json(None, bot) is None
|
||||
|
||||
|
@ -98,6 +104,7 @@ class TestExternalReplyInfoWithoutRequest(TestExternalReplyInfoBase):
|
|||
assert ext_reply_info_dict["message_id"] == self.message_id
|
||||
assert ext_reply_info_dict["link_preview_options"] == self.link_preview_options.to_dict()
|
||||
assert ext_reply_info_dict["giveaway"] == self.giveaway.to_dict()
|
||||
assert ext_reply_info_dict["paid_media"] == self.paid_media.to_dict()
|
||||
|
||||
def test_equality(self, external_reply_info):
|
||||
a = external_reply_info
|
||||
|
|
|
@ -33,6 +33,7 @@ from telegram import (
|
|||
TransactionPartner,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
TransactionPartnerUser,
|
||||
User,
|
||||
)
|
||||
|
@ -101,6 +102,7 @@ def star_transactions():
|
|||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.USER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
],
|
||||
)
|
||||
def tp_scope_type(request):
|
||||
|
@ -113,11 +115,13 @@ def tp_scope_type(request):
|
|||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerUser,
|
||||
TransactionPartnerTelegramAds,
|
||||
],
|
||||
ids=[
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.USER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
],
|
||||
)
|
||||
def tp_scope_class(request):
|
||||
|
@ -130,11 +134,13 @@ def tp_scope_class(request):
|
|||
(TransactionPartnerFragment, TransactionPartner.FRAGMENT),
|
||||
(TransactionPartnerOther, TransactionPartner.OTHER),
|
||||
(TransactionPartnerUser, TransactionPartner.USER),
|
||||
(TransactionPartnerTelegramAds, TransactionPartner.TELEGRAM_ADS),
|
||||
],
|
||||
ids=[
|
||||
TransactionPartner.FRAGMENT,
|
||||
TransactionPartner.OTHER,
|
||||
TransactionPartner.USER,
|
||||
TransactionPartner.TELEGRAM_ADS,
|
||||
],
|
||||
)
|
||||
def tp_scope_class_and_type(request):
|
||||
|
@ -147,6 +153,7 @@ def transaction_partner(tp_scope_class_and_type):
|
|||
return tp_scope_class_and_type[0].de_json(
|
||||
{
|
||||
"type": tp_scope_class_and_type[1],
|
||||
"invoice_payload": TestTransactionPartnerBase.invoice_payload,
|
||||
"withdrawal_state": TestTransactionPartnerBase.withdrawal_state.to_dict(),
|
||||
"user": TestTransactionPartnerBase.user.to_dict(),
|
||||
},
|
||||
|
@ -244,6 +251,7 @@ class TestStarTransactionWithoutRequest(TestStarTransactionBase):
|
|||
}
|
||||
st = StarTransaction.de_json(json_dict, bot)
|
||||
st_none = StarTransaction.de_json(None, bot)
|
||||
assert st.api_kwargs == {}
|
||||
assert st.id == self.id
|
||||
assert st.amount == self.amount
|
||||
assert st.date == from_timestamp(self.date)
|
||||
|
@ -329,6 +337,7 @@ class TestStarTransactionsWithoutRequest(TestStarTransactionsBase):
|
|||
}
|
||||
st = StarTransactions.de_json(json_dict, bot)
|
||||
st_none = StarTransactions.de_json(None, bot)
|
||||
assert st.api_kwargs == {}
|
||||
assert st.transactions == tuple(self.transactions)
|
||||
assert st_none is None
|
||||
|
||||
|
@ -359,6 +368,7 @@ class TestStarTransactionsWithoutRequest(TestStarTransactionsBase):
|
|||
class TestTransactionPartnerBase:
|
||||
withdrawal_state = withdrawal_state_succeeded()
|
||||
user = transaction_partner_user().user
|
||||
invoice_payload = "payload"
|
||||
|
||||
|
||||
class TestTransactionPartnerWithoutRequest(TestTransactionPartnerBase):
|
||||
|
@ -374,11 +384,14 @@ class TestTransactionPartnerWithoutRequest(TestTransactionPartnerBase):
|
|||
|
||||
json_dict = {
|
||||
"type": type_,
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
}
|
||||
tp = TransactionPartner.de_json(json_dict, bot)
|
||||
assert set(tp.api_kwargs.keys()) == {"user", "withdrawal_state"} - set(cls.__slots__)
|
||||
assert set(tp.api_kwargs.keys()) == {"user", "withdrawal_state", "invoice_payload"} - set(
|
||||
cls.__slots__
|
||||
)
|
||||
|
||||
assert isinstance(tp, TransactionPartner)
|
||||
assert type(tp) is cls
|
||||
|
@ -387,6 +400,7 @@ class TestTransactionPartnerWithoutRequest(TestTransactionPartnerBase):
|
|||
assert tp.withdrawal_state == self.withdrawal_state
|
||||
if "user" in cls.__slots__:
|
||||
assert tp.user == self.user
|
||||
assert tp.invoice_payload == self.invoice_payload
|
||||
|
||||
assert cls.de_json(None, bot) is None
|
||||
assert TransactionPartner.de_json({}, bot) is None
|
||||
|
@ -394,6 +408,7 @@ class TestTransactionPartnerWithoutRequest(TestTransactionPartnerBase):
|
|||
def test_de_json_invalid_type(self, bot):
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
}
|
||||
|
@ -401,6 +416,7 @@ class TestTransactionPartnerWithoutRequest(TestTransactionPartnerBase):
|
|||
assert tp.api_kwargs == {
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
"invoice_payload": self.invoice_payload,
|
||||
}
|
||||
|
||||
assert type(tp) is TransactionPartner
|
||||
|
@ -411,6 +427,7 @@ class TestTransactionPartnerWithoutRequest(TestTransactionPartnerBase):
|
|||
TransactionPartnerFragment instance."""
|
||||
json_dict = {
|
||||
"type": "invalid",
|
||||
"invoice_payload": self.invoice_payload,
|
||||
"withdrawal_state": self.withdrawal_state.to_dict(),
|
||||
"user": self.user.to_dict(),
|
||||
}
|
||||
|
@ -423,6 +440,7 @@ class TestTransactionPartnerWithoutRequest(TestTransactionPartnerBase):
|
|||
assert tp_dict["type"] == transaction_partner.type
|
||||
if hasattr(transaction_partner, "user"):
|
||||
assert tp_dict["user"] == transaction_partner.user.to_dict()
|
||||
assert tp_dict["invoice_payload"] == transaction_partner.invoice_payload
|
||||
if hasattr(transaction_partner, "withdrawal_state"):
|
||||
assert tp_dict["withdrawal_state"] == transaction_partner.withdrawal_state.to_dict()
|
||||
|
||||
|
|
Loading…
Reference in a new issue