mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-03-14 11:43:50 +01:00
API 6.1 (#3117)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
This commit is contained in:
parent
7a7465e8e2
commit
debe86aea2
18 changed files with 402 additions and 14 deletions
|
@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
|
|||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.0-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.1-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
|
@ -112,7 +112,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
|||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **6.0** are supported.
|
||||
All types and methods of the Telegram Bot API **6.1** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
|
@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
|
|||
:target: https://pypi.org/project/python-telegram-bot-raw/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.0-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.1-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
|
@ -105,7 +105,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
|||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **6.0** are supported.
|
||||
All types and methods of the Telegram Bot API **6.1** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
|
@ -30,6 +30,7 @@ filterwarnings =
|
|||
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
|
||||
; and instead do a trick directly in tests/conftest.py
|
||||
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
|
||||
markers = dev: If you want to test a specific test, use this
|
||||
|
||||
[coverage:run]
|
||||
branch = True
|
||||
|
|
|
@ -41,7 +41,7 @@ def print_ver_info() -> None: # skipcq: PY-D0003
|
|||
git_revision = _git_revision()
|
||||
print(f'python-telegram-bot {telegram_ver}' + (f' ({git_revision})' if git_revision else ''))
|
||||
print(f'Bot API {BOT_API_VERSION}')
|
||||
print(f'certifi {certifi.__version__}') # type: ignore[attr-defined]
|
||||
print('certifi' + certifi.__version__)
|
||||
sys_version = sys.version.replace('\n', ' ')
|
||||
print(f'Python {sys_version}')
|
||||
|
||||
|
|
146
telegram/bot.py
146
telegram/bot.py
|
@ -3080,6 +3080,7 @@ class Bot(TelegramObject):
|
|||
api_kwargs: JSONDict = None,
|
||||
ip_address: str = None,
|
||||
drop_pending_updates: bool = None,
|
||||
secret_token: str = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to specify a url and receive incoming updates via an outgoing webhook.
|
||||
|
@ -3087,9 +3088,9 @@ class Bot(TelegramObject):
|
|||
specified url, containing a JSON-serialized Update. In case of an unsuccessful request,
|
||||
Telegram will give up after a reasonable amount of attempts.
|
||||
|
||||
If you'd like to make sure that the Webhook request comes from Telegram, Telegram
|
||||
recommends using a secret path in the URL, e.g. https://www.example.com/<token>. Since
|
||||
nobody else knows your bot's token, you can be pretty sure it's us.
|
||||
If you'd like to make sure that the Webhook was set by you, you can specify secret data in
|
||||
the parameter ``secret_token``. If specified, the request will contain a header
|
||||
``X-Telegram-Bot-Api-Secret-Token`` with the secret token as content.
|
||||
|
||||
Note:
|
||||
The certificate argument should be a file from disk ``open(filename, 'rb')``.
|
||||
|
@ -3117,6 +3118,12 @@ class Bot(TelegramObject):
|
|||
a short period of time.
|
||||
drop_pending_updates (:obj:`bool`, optional): Pass :obj:`True` to drop all pending
|
||||
updates.
|
||||
secret_token (:obj:`str`, optional): A secret token to be sent in a header
|
||||
``X-Telegram-Bot-Api-Secret-Token`` in every webhook request, 1-256 characters.
|
||||
Only characters ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed.
|
||||
The header is useful to ensure that the request comes from a webhook set by you.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
|
@ -3157,6 +3164,8 @@ class Bot(TelegramObject):
|
|||
data['ip_address'] = ip_address
|
||||
if drop_pending_updates:
|
||||
data['drop_pending_updates'] = drop_pending_updates
|
||||
if secret_token is not None:
|
||||
data["secret_token"] = secret_token
|
||||
|
||||
result = self._post('setWebhook', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
|
@ -5914,6 +5923,135 @@ class Bot(TelegramObject):
|
|||
)
|
||||
return MenuButton.de_json(result, bot=self) # type: ignore[return-value, arg-type]
|
||||
|
||||
@log
|
||||
def create_invoice_link(
|
||||
self,
|
||||
title: str,
|
||||
description: str,
|
||||
payload: str,
|
||||
provider_token: str,
|
||||
currency: str,
|
||||
prices: List["LabeledPrice"],
|
||||
max_tip_amount: int = None,
|
||||
suggested_tip_amounts: List[int] = None,
|
||||
provider_data: Union[str, object] = None,
|
||||
photo_url: str = None,
|
||||
photo_size: int = None,
|
||||
photo_width: int = None,
|
||||
photo_height: int = None,
|
||||
need_name: bool = None,
|
||||
need_phone_number: bool = None,
|
||||
need_email: bool = None,
|
||||
need_shipping_address: bool = None,
|
||||
send_phone_number_to_provider: bool = None,
|
||||
send_email_to_provider: bool = None,
|
||||
is_flexible: bool = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> str:
|
||||
"""Use this method to create a link for an invoice.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
|
||||
Args:
|
||||
title (:obj:`str`): Product name. 1-32 characters.
|
||||
description (:obj:`str`): Product description. 1-255 characters.
|
||||
payload (:obj:`str`): Bot-defined invoice payload. 1-128 bytes. This will not be
|
||||
displayed to the user, use for your internal processes.
|
||||
provider_token (:obj:`str`): Payments provider token, obtained via
|
||||
`@BotFather <https://t.me/BotFather>`_.
|
||||
currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies
|
||||
<https://core.telegram.org/bots/payments#supported-currencies>`_.
|
||||
prices (List[:class:`telegram.LabeledPrice`)]: Price breakdown, a list
|
||||
of components (e.g. product price, tax, discount, delivery cost, delivery tax,
|
||||
bonus, etc.).
|
||||
max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the
|
||||
*smallest* units of the currency (integer, **not** float/double). For example, for
|
||||
a maximum tip of US$ 1.45 pass ``max_tip_amount = 145``. See the exp parameter in
|
||||
`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). Defaults to ``0``.
|
||||
suggested_tip_amounts (List[:obj:`int`], optional): An array of
|
||||
suggested amounts of tips in the *smallest* units of the currency (integer, **not**
|
||||
float/double). At most 4 suggested tip amounts can be specified. The suggested tip
|
||||
amounts must be positive, passed in a strictly increased order and must not exceed
|
||||
``max_tip_amount``.
|
||||
provider_data (:obj:`str` | :obj:`object`, optional): Data about the
|
||||
invoice, which will be shared with the payment provider. A detailed description of
|
||||
required fields should be provided by the payment provider. When an object is
|
||||
passed, it will be encoded as JSON.
|
||||
photo_url (:obj:`str`, optional): URL of the product photo for the invoice. Can be a
|
||||
photo of the goods or a marketing image for a service.
|
||||
photo_size (:obj:`int`, optional): Photo size in bytes.
|
||||
photo_width (:obj:`int`, optional): Photo width.
|
||||
photo_height (:obj:`int`, optional): Photo height.
|
||||
need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full
|
||||
name to complete the order.
|
||||
need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's
|
||||
phone number to complete the order.
|
||||
need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email
|
||||
address to complete the order.
|
||||
need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the
|
||||
user's shipping address to complete the order.
|
||||
send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's
|
||||
phone number should be sent to provider.
|
||||
send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email
|
||||
address should be sent to provider.
|
||||
is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on
|
||||
the shipping method.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:class:`str`: On success, the created invoice link is returned.
|
||||
"""
|
||||
data: JSONDict = {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"payload": payload,
|
||||
"provider_token": provider_token,
|
||||
"currency": currency,
|
||||
"prices": [p.to_dict() for p in prices],
|
||||
}
|
||||
if max_tip_amount is not None:
|
||||
data["max_tip_amount"] = max_tip_amount
|
||||
if suggested_tip_amounts is not None:
|
||||
data["suggested_tip_amounts"] = suggested_tip_amounts
|
||||
if provider_data is not None:
|
||||
data["provider_data"] = provider_data
|
||||
if photo_url is not None:
|
||||
data["photo_url"] = photo_url
|
||||
if photo_size is not None:
|
||||
data["photo_size"] = photo_size
|
||||
if photo_width is not None:
|
||||
data["photo_width"] = photo_width
|
||||
if photo_height is not None:
|
||||
data["photo_height"] = photo_height
|
||||
if need_name is not None:
|
||||
data["need_name"] = need_name
|
||||
if need_phone_number is not None:
|
||||
data["need_phone_number"] = need_phone_number
|
||||
if need_email is not None:
|
||||
data["need_email"] = need_email
|
||||
if need_shipping_address is not None:
|
||||
data["need_shipping_address"] = need_shipping_address
|
||||
if is_flexible is not None:
|
||||
data["is_flexible"] = is_flexible
|
||||
if send_phone_number_to_provider is not None:
|
||||
data["send_phone_number_to_provider"] = send_phone_number_to_provider
|
||||
if send_email_to_provider is not None:
|
||||
data["send_email_to_provider"] = send_email_to_provider
|
||||
|
||||
return self._post( # type: ignore[return-value]
|
||||
"createInvoiceLink",
|
||||
data,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data: JSONDict = {'id': self.id, 'username': self.username, 'first_name': self.first_name}
|
||||
|
@ -6106,3 +6244,5 @@ class Bot(TelegramObject):
|
|||
"""Alias for :meth:`get_my_default_administrator_rights`"""
|
||||
setMyDefaultAdministratorRights = set_my_default_administrator_rights
|
||||
"""Alias for :meth:`set_my_default_administrator_rights`"""
|
||||
createInvoiceLink = create_invoice_link
|
||||
"""Alias for :meth:`create_invoice_link`"""
|
||||
|
|
|
@ -116,6 +116,16 @@ class Chat(TelegramObject):
|
|||
chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which
|
||||
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
join_to_send_messages (:obj:`bool`, optional): :obj:`True`, if users need to join the
|
||||
supergroup before they can send messages. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
join_by_request (:obj:`bool`, optional): :obj:`True`, if all users directly joining the
|
||||
supergroup need to be approved by supergroup administrators. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
|
@ -160,7 +170,16 @@ class Chat(TelegramObject):
|
|||
chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which
|
||||
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
join_to_send_messages (:obj:`bool`): Optional. :obj:`True`, if users need to join the
|
||||
supergroup before they can send messages. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
join_by_request (:obj:`bool`): Optional. :obj:`True`, if all users directly joining the
|
||||
supergroup need to be approved by supergroup administrators. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
|
@ -186,6 +205,8 @@ class Chat(TelegramObject):
|
|||
'message_auto_delete_time',
|
||||
'has_protected_content',
|
||||
'has_private_forwards',
|
||||
'join_to_send_messages',
|
||||
'join_by_request',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
|
@ -226,6 +247,8 @@ class Chat(TelegramObject):
|
|||
message_auto_delete_time: int = None,
|
||||
has_private_forwards: bool = None,
|
||||
has_protected_content: bool = None,
|
||||
join_to_send_messages: bool = None,
|
||||
join_by_request: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
|
@ -254,6 +277,8 @@ class Chat(TelegramObject):
|
|||
self.can_set_sticker_set = can_set_sticker_set
|
||||
self.linked_chat_id = linked_chat_id
|
||||
self.location = location
|
||||
self.join_to_send_messages = join_to_send_messages
|
||||
self.join_by_request = join_by_request
|
||||
|
||||
self.bot = bot
|
||||
self._id_attrs = (self.id,)
|
||||
|
|
|
@ -21,7 +21,7 @@ The following constants were extracted from the
|
|||
`Telegram Bots API <https://core.telegram.org/bots/api>`_.
|
||||
|
||||
Attributes:
|
||||
BOT_API_VERSION (:obj:`str`): `6.0`. Telegram Bot API version supported by this
|
||||
BOT_API_VERSION (:obj:`str`): `6.1`. Telegram Bot API version supported by this
|
||||
version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
@ -247,7 +247,7 @@ Attributes:
|
|||
"""
|
||||
from typing import List
|
||||
|
||||
BOT_API_VERSION: str = '6.0'
|
||||
BOT_API_VERSION: str = '6.1'
|
||||
MAX_MESSAGE_LENGTH: int = 4096
|
||||
MAX_CAPTION_LENGTH: int = 1024
|
||||
ANONYMOUS_ADMIN_ID: int = 1087968824
|
||||
|
|
|
@ -1004,6 +1004,38 @@ officedocument.wordprocessingml.document")``.
|
|||
location = _Location()
|
||||
"""Messages that contain :class:`telegram.Location`."""
|
||||
|
||||
class _UserAttachment(UpdateFilter):
|
||||
__slots__ = ()
|
||||
name = "Filters.user_attachment"
|
||||
|
||||
def filter(self, update: Update) -> bool:
|
||||
return bool(update.effective_user) and bool(
|
||||
update.effective_user.added_to_attachment_menu
|
||||
)
|
||||
|
||||
user_attachment = _UserAttachment()
|
||||
"""This filter filters *any* message that have a user who added the bot to their
|
||||
:attr:`attachment menu <telegram.User.added_to_attachment_menu>` as
|
||||
:attr:`telegram.Update.effective_user`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
"""
|
||||
|
||||
class _UserPremium(UpdateFilter):
|
||||
__slots__ = ()
|
||||
name = "Filters.premium_user"
|
||||
|
||||
def filter(self, update: Update) -> bool:
|
||||
return bool(update.effective_user) and bool(update.effective_user.is_premium)
|
||||
|
||||
premium_user = _UserPremium()
|
||||
"""This filter filters *any* message from a
|
||||
:attr:`Telegram Premium user <telegram.User.is_premium>` as
|
||||
:attr:`telegram.Update.effective_user`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
"""
|
||||
|
||||
class _Venue(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.venue'
|
||||
|
|
|
@ -463,6 +463,11 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
application. Else, the webhook will be started on
|
||||
https://listen:port/url_path. Also calls :meth:`telegram.Bot.set_webhook` as required.
|
||||
|
||||
Note:
|
||||
``telegram.Bot.set_webhook.secret_token`` is not checked by this webhook
|
||||
implementation. If you want to use this new security parameter, either build your own
|
||||
webhook server or update your code to version 20.0a2+.
|
||||
|
||||
.. versionchanged:: 13.4
|
||||
:meth:`start_webhook` now *always* calls :meth:`telegram.Bot.set_webhook`, so pass
|
||||
``webhook_url`` instead of calling ``updater.bot.set_webhook(webhook_url)`` manually.
|
||||
|
|
|
@ -60,6 +60,10 @@ class Sticker(TelegramObject):
|
|||
position where the mask should be placed.
|
||||
file_size (:obj:`int`, optional): File size.
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
premium_animation (:class:`telegram.File`, optional): Premium animation for the sticker,
|
||||
if the sticker is premium.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
**kwargs (obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
|
@ -80,6 +84,10 @@ class Sticker(TelegramObject):
|
|||
mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position
|
||||
where the mask should be placed.
|
||||
file_size (:obj:`int`): Optional. File size.
|
||||
premium_animation (:class:`telegram.File`): Optional. Premium animation for the sticker,
|
||||
if the sticker is premium.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
|
||||
"""
|
||||
|
@ -97,6 +105,7 @@ class Sticker(TelegramObject):
|
|||
'height',
|
||||
'file_unique_id',
|
||||
'emoji',
|
||||
'premium_animation',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
|
@ -114,6 +123,7 @@ class Sticker(TelegramObject):
|
|||
set_name: str = None,
|
||||
mask_position: 'MaskPosition' = None,
|
||||
bot: 'Bot' = None,
|
||||
premium_animation: 'File' = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
|
@ -130,12 +140,17 @@ class Sticker(TelegramObject):
|
|||
self.set_name = set_name
|
||||
self.mask_position = mask_position
|
||||
self.bot = bot
|
||||
self.premium_animation = premium_animation
|
||||
|
||||
self._id_attrs = (self.file_unique_id,)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Sticker']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
# needs to be here to avoid circular imports
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from telegram import File
|
||||
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
|
@ -143,6 +158,7 @@ class Sticker(TelegramObject):
|
|||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
data['mask_position'] = MaskPosition.de_json(data.get('mask_position'), bot)
|
||||
data["premium_animation"] = File.de_json(data.get("premium_animation"), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class LoginUrl(TelegramObject):
|
|||
`Checking authorization <https://core.telegram.org/widgets/login#checking-authorization>`_
|
||||
|
||||
Args:
|
||||
url (:obj:`str`): An HTTP URL to be opened with user authorization data added to the query
|
||||
url (:obj:`str`): An HTTPS URL to be opened with user authorization data added to the query
|
||||
string when the button is pressed. If the user refuses to provide authorization data,
|
||||
the original URL without information about the user will be opened. The data added is
|
||||
the same as described in
|
||||
|
@ -60,7 +60,7 @@ class LoginUrl(TelegramObject):
|
|||
for your bot to send messages to the user.
|
||||
|
||||
Attributes:
|
||||
url (:obj:`str`): An HTTP URL to be opened with user authorization data.
|
||||
url (:obj:`str`): An HTTPS URL to be opened with user authorization data.
|
||||
forward_text (:obj:`str`): Optional. New text of the button in forwarded messages.
|
||||
bot_username (:obj:`str`): Optional. Username of a bot, which will be used for user
|
||||
authorization.
|
||||
|
|
|
@ -79,6 +79,13 @@ class User(TelegramObject):
|
|||
supports_inline_queries (:obj:`str`, optional): :obj:`True`, if the bot supports inline
|
||||
queries. Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
is_premium (:obj:`bool`, optional): :obj:`True`, if this user is a Telegram Premium user.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
added_to_attachment_menu (:obj:`bool`, optional): :obj:`True`, if this user added
|
||||
the bot to the attachment menu.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
|
||||
Attributes:
|
||||
id (:obj:`int`): Unique identifier for this user or bot.
|
||||
|
@ -94,6 +101,14 @@ class User(TelegramObject):
|
|||
supports_inline_queries (:obj:`str`): Optional. :obj:`True`, if the bot supports inline
|
||||
queries. Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
is_premium (:obj:`bool`): Optional. :obj:`True`, if this user is a Telegram
|
||||
Premium user.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
added_to_attachment_menu (:obj:`bool`): Optional. :obj:`True`, if this user added
|
||||
the bot to the attachment menu.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
|
||||
"""
|
||||
|
||||
|
@ -108,6 +123,8 @@ class User(TelegramObject):
|
|||
'id',
|
||||
'bot',
|
||||
'language_code',
|
||||
'is_premium',
|
||||
'added_to_attachment_menu',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
|
@ -123,6 +140,8 @@ class User(TelegramObject):
|
|||
can_read_all_group_messages: bool = None,
|
||||
supports_inline_queries: bool = None,
|
||||
bot: 'Bot' = None,
|
||||
is_premium: bool = None,
|
||||
added_to_attachment_menu: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
|
@ -136,6 +155,8 @@ class User(TelegramObject):
|
|||
self.can_join_groups = can_join_groups
|
||||
self.can_read_all_group_messages = can_read_all_group_messages
|
||||
self.supports_inline_queries = supports_inline_queries
|
||||
self.is_premium = is_premium
|
||||
self.added_to_attachment_menu = added_to_attachment_menu
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
|
|
@ -1410,6 +1410,32 @@ class TestBot:
|
|||
assert bot.set_webhook(drop_pending_updates=drop_pending_updates)
|
||||
assert bot.delete_webhook(drop_pending_updates=drop_pending_updates)
|
||||
|
||||
def test_set_webhook_params(self, bot, monkeypatch):
|
||||
# actually making calls to TG is done in
|
||||
# test_set_webhook_get_webhook_info_and_delete_webhook. Sadly secret_token can't be tested
|
||||
# there so we have this function \o/
|
||||
def make_assertion(*args, **_):
|
||||
kwargs = args[1]
|
||||
return (
|
||||
kwargs["url"] == "example.com"
|
||||
and kwargs["max_connections"] == 7
|
||||
and kwargs["allowed_updates"] == ["messages"]
|
||||
and kwargs["ip_address"] == "127.0.0.1"
|
||||
and kwargs["drop_pending_updates"]
|
||||
and kwargs["secret_token"] == "SoSecretToken"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
|
||||
assert bot.set_webhook(
|
||||
"example.com",
|
||||
max_connections=7,
|
||||
allowed_updates=["messages"],
|
||||
ip_address="127.0.0.1",
|
||||
drop_pending_updates=True,
|
||||
secret_token="SoSecretToken",
|
||||
)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_leave_chat(self, bot):
|
||||
with pytest.raises(BadRequest, match='Chat not found'):
|
||||
|
|
|
@ -43,6 +43,8 @@ def chat(bot):
|
|||
location=TestChat.location,
|
||||
has_private_forwards=True,
|
||||
has_protected_content=True,
|
||||
join_to_send_messages=True,
|
||||
join_by_request=True,
|
||||
)
|
||||
|
||||
|
||||
|
@ -66,6 +68,8 @@ class TestChat:
|
|||
location = ChatLocation(Location(123, 456), 'Barbie World')
|
||||
has_protected_content = True
|
||||
has_private_forwards = True
|
||||
join_to_send_messages = True
|
||||
join_by_request = True
|
||||
|
||||
def test_slot_behaviour(self, chat, recwarn, mro_slots):
|
||||
for attr in chat.__slots__:
|
||||
|
@ -92,6 +96,8 @@ class TestChat:
|
|||
'has_private_forwards': self.has_private_forwards,
|
||||
'linked_chat_id': self.linked_chat_id,
|
||||
'location': self.location.to_dict(),
|
||||
'join_to_send_messages': self.join_to_send_messages,
|
||||
'join_by_request': self.join_by_request,
|
||||
}
|
||||
chat = Chat.de_json(json_dict, bot)
|
||||
|
||||
|
@ -111,6 +117,8 @@ class TestChat:
|
|||
assert chat.linked_chat_id == self.linked_chat_id
|
||||
assert chat.location.location == self.location.location
|
||||
assert chat.location.address == self.location.address
|
||||
assert chat.join_to_send_messages == self.join_to_send_messages
|
||||
assert chat.join_by_request == self.join_by_request
|
||||
|
||||
def test_to_dict(self, chat):
|
||||
chat_dict = chat.to_dict()
|
||||
|
@ -129,6 +137,8 @@ class TestChat:
|
|||
assert chat_dict['has_protected_content'] == chat.has_protected_content
|
||||
assert chat_dict['linked_chat_id'] == chat.linked_chat_id
|
||||
assert chat_dict['location'] == chat.location.to_dict()
|
||||
assert chat_dict["join_to_send_messages"] == chat.join_to_send_messages
|
||||
assert chat_dict["join_by_request"] == chat.join_by_request
|
||||
|
||||
def test_link(self, chat):
|
||||
assert chat.link == f'https://t.me/{chat.username}'
|
||||
|
|
|
@ -1189,6 +1189,19 @@ class TestFilters:
|
|||
with pytest.raises(RuntimeError, match='Cannot set name'):
|
||||
f.name = 'foo'
|
||||
|
||||
def test_filters_user_attributes(self, update):
|
||||
assert not Filters.user_attachment(update)
|
||||
assert not Filters.premium_user(update)
|
||||
update.message.from_user.added_to_attachment_menu = True
|
||||
assert Filters.user_attachment(update)
|
||||
assert not Filters.premium_user(update)
|
||||
update.message.from_user.is_premium = True
|
||||
assert Filters.user_attachment(update)
|
||||
assert Filters.premium_user(update)
|
||||
update.message.from_user.added_to_attachment_menu = False
|
||||
assert not Filters.user_attachment(update)
|
||||
assert Filters.premium_user(update)
|
||||
|
||||
def test_filters_chat_init(self):
|
||||
with pytest.raises(RuntimeError, match='in conjunction with'):
|
||||
Filters.chat(chat_id=1, username='chat')
|
||||
|
|
|
@ -100,8 +100,19 @@ class TestInvoice:
|
|||
assert message.invoice.title == self.title
|
||||
assert message.invoice.total_amount == self.total_amount
|
||||
|
||||
link = bot.create_invoice_link(
|
||||
title=self.title,
|
||||
description=self.description,
|
||||
payload=self.payload,
|
||||
provider_token=provider_token,
|
||||
currency=self.currency,
|
||||
prices=self.prices,
|
||||
)
|
||||
assert isinstance(link, str)
|
||||
assert link != ""
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_send_all_args(self, bot, chat_id, provider_token, monkeypatch):
|
||||
def test_send_all_args_send_invoice(self, bot, chat_id, provider_token, monkeypatch):
|
||||
message = bot.send_invoice(
|
||||
chat_id,
|
||||
self.title,
|
||||
|
@ -195,6 +206,56 @@ class TestInvoice:
|
|||
protect_content=True,
|
||||
)
|
||||
|
||||
def test_send_all_args_create_invoice_link(self, bot, chat_id, provider_token, monkeypatch):
|
||||
def make_assertion(*args, **_):
|
||||
kwargs = args[1]
|
||||
return (
|
||||
kwargs["title"] == "title"
|
||||
and kwargs["description"] == "description"
|
||||
and kwargs["payload"] == "payload"
|
||||
and kwargs["provider_token"] == "provider_token"
|
||||
and kwargs["currency"] == "currency"
|
||||
and kwargs["prices"] == [p.to_dict() for p in self.prices]
|
||||
and kwargs["max_tip_amount"] == "max_tip_amount"
|
||||
and kwargs["suggested_tip_amounts"] == "suggested_tip_amounts"
|
||||
and kwargs["provider_data"] == "provider_data"
|
||||
and kwargs["photo_url"] == "photo_url"
|
||||
and kwargs["photo_size"] == "photo_size"
|
||||
and kwargs["photo_width"] == "photo_width"
|
||||
and kwargs["photo_height"] == "photo_height"
|
||||
and kwargs["need_name"] == "need_name"
|
||||
and kwargs["need_phone_number"] == "need_phone_number"
|
||||
and kwargs["need_email"] == "need_email"
|
||||
and kwargs["need_shipping_address"] == "need_shipping_address"
|
||||
and kwargs["send_phone_number_to_provider"] == "send_phone_number_to_provider"
|
||||
and kwargs["send_email_to_provider"] == "send_email_to_provider"
|
||||
and kwargs["is_flexible"] == "is_flexible"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
assert bot.create_invoice_link(
|
||||
title="title",
|
||||
description="description",
|
||||
payload="payload",
|
||||
provider_token="provider_token",
|
||||
currency="currency",
|
||||
prices=self.prices,
|
||||
max_tip_amount="max_tip_amount",
|
||||
suggested_tip_amounts="suggested_tip_amounts",
|
||||
provider_data="provider_data",
|
||||
photo_url="photo_url",
|
||||
photo_size="photo_size",
|
||||
photo_width="photo_width",
|
||||
photo_height="photo_height",
|
||||
need_name="need_name",
|
||||
need_phone_number="need_phone_number",
|
||||
need_email="need_email",
|
||||
need_shipping_address="need_shipping_address",
|
||||
send_phone_number_to_provider="send_phone_number_to_provider",
|
||||
send_email_to_provider="send_email_to_provider",
|
||||
is_flexible="is_flexible",
|
||||
)
|
||||
|
||||
def test_send_object_as_provider_data(self, monkeypatch, bot, chat_id, provider_token):
|
||||
def test(url, data, **kwargs):
|
||||
# depends on whether we're using ujson
|
||||
|
|
|
@ -23,7 +23,7 @@ from time import sleep
|
|||
import pytest
|
||||
from flaky import flaky
|
||||
|
||||
from telegram import Sticker, PhotoSize, TelegramError, StickerSet, Audio, MaskPosition, Bot
|
||||
from telegram import Sticker, PhotoSize, TelegramError, StickerSet, Audio, MaskPosition, Bot, File
|
||||
from telegram.error import BadRequest
|
||||
from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling
|
||||
|
||||
|
@ -87,6 +87,8 @@ class TestSticker:
|
|||
sticker_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
|
||||
sticker_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
|
||||
|
||||
premium_animation = File("this_is_an_id", "this_is_an_unique_id")
|
||||
|
||||
def test_slot_behaviour(self, sticker, mro_slots, recwarn):
|
||||
for attr in sticker.__slots__:
|
||||
assert getattr(sticker, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
|
@ -117,6 +119,8 @@ class TestSticker:
|
|||
assert sticker.thumb.width == self.thumb_width
|
||||
assert sticker.thumb.height == self.thumb_height
|
||||
assert sticker.thumb.file_size == self.thumb_file_size
|
||||
# we need to be a premium TG user to send a premium sticker, so the below is not tested
|
||||
# assert sticker.premium_animation == self.premium_animation
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_send_all_args(self, bot, chat_id, sticker_file, sticker):
|
||||
|
@ -134,6 +138,8 @@ class TestSticker:
|
|||
assert message.sticker.is_animated == sticker.is_animated
|
||||
assert message.sticker.is_video == sticker.is_video
|
||||
assert message.sticker.file_size == sticker.file_size
|
||||
# we need to be a premium TG user to send a premium sticker, so the below is not tested
|
||||
# assert message.sticker.premium_animation == sticker.premium_animation
|
||||
|
||||
assert isinstance(message.sticker.thumb, PhotoSize)
|
||||
assert isinstance(message.sticker.thumb.file_id, str)
|
||||
|
@ -207,6 +213,7 @@ class TestSticker:
|
|||
'thumb': sticker.thumb.to_dict(),
|
||||
'emoji': self.emoji,
|
||||
'file_size': self.file_size,
|
||||
'premium_animation': self.premium_animation.to_dict(),
|
||||
}
|
||||
json_sticker = Sticker.de_json(json_dict, bot)
|
||||
|
||||
|
@ -219,6 +226,7 @@ class TestSticker:
|
|||
assert json_sticker.emoji == self.emoji
|
||||
assert json_sticker.file_size == self.file_size
|
||||
assert json_sticker.thumb == sticker.thumb
|
||||
assert json_sticker.premium_animation == self.premium_animation
|
||||
|
||||
def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker):
|
||||
def test(url, data, **kwargs):
|
||||
|
@ -304,6 +312,24 @@ class TestSticker:
|
|||
with pytest.raises(TypeError):
|
||||
bot.send_sticker(chat_id)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_premium_animation(self, bot):
|
||||
# testing animation sucks a bit since we can't create a premium sticker. What we can do is
|
||||
# get a sticker set which includes a premium sticker and check that specific one.
|
||||
premium_sticker_set = bot.get_sticker_set("Flame")
|
||||
# the first one to appear here is a sticker with unique file id of AQADOBwAAifPOElr
|
||||
# this could change in the future ofc.
|
||||
premium_sticker = premium_sticker_set.stickers[20]
|
||||
assert premium_sticker.premium_animation.file_unique_id == "AQADOBwAAifPOElr"
|
||||
assert isinstance(premium_sticker.premium_animation.file_id, str)
|
||||
assert premium_sticker.premium_animation.file_id != ""
|
||||
premium_sticker_dict = {
|
||||
"file_unique_id": "AQADOBwAAifPOElr",
|
||||
"file_id": premium_sticker.premium_animation.file_id,
|
||||
"file_size": premium_sticker.premium_animation.file_size,
|
||||
}
|
||||
assert premium_sticker.premium_animation.to_dict() == premium_sticker_dict
|
||||
|
||||
def test_equality(self, sticker):
|
||||
a = Sticker(
|
||||
sticker.file_id,
|
||||
|
|
|
@ -35,6 +35,8 @@ def json_dict():
|
|||
'can_join_groups': TestUser.can_join_groups,
|
||||
'can_read_all_group_messages': TestUser.can_read_all_group_messages,
|
||||
'supports_inline_queries': TestUser.supports_inline_queries,
|
||||
'is_premium': TestUser.is_premium,
|
||||
'added_to_attachment_menu': TestUser.added_to_attachment_menu,
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,6 +53,8 @@ def user(bot):
|
|||
can_read_all_group_messages=TestUser.can_read_all_group_messages,
|
||||
supports_inline_queries=TestUser.supports_inline_queries,
|
||||
bot=bot,
|
||||
is_premium=TestUser.is_premium,
|
||||
added_to_attachment_menu=TestUser.added_to_attachment_menu,
|
||||
)
|
||||
|
||||
|
||||
|
@ -64,6 +68,8 @@ class TestUser:
|
|||
can_join_groups = True
|
||||
can_read_all_group_messages = True
|
||||
supports_inline_queries = False
|
||||
is_premium = True
|
||||
added_to_attachment_menu = False
|
||||
|
||||
def test_slot_behaviour(self, user, mro_slots, recwarn):
|
||||
for attr in user.__slots__:
|
||||
|
@ -85,6 +91,8 @@ class TestUser:
|
|||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
assert user.is_premium == self.is_premium
|
||||
assert user.added_to_attachment_menu == self.added_to_attachment_menu
|
||||
|
||||
def test_de_json_without_username(self, json_dict, bot):
|
||||
del json_dict['username']
|
||||
|
@ -100,6 +108,8 @@ class TestUser:
|
|||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
assert user.is_premium == self.is_premium
|
||||
assert user.added_to_attachment_menu == self.added_to_attachment_menu
|
||||
|
||||
def test_de_json_without_username_and_last_name(self, json_dict, bot):
|
||||
del json_dict['username']
|
||||
|
@ -116,6 +126,8 @@ class TestUser:
|
|||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
assert user.is_premium == self.is_premium
|
||||
assert user.added_to_attachment_menu == self.added_to_attachment_menu
|
||||
|
||||
def test_name(self, user):
|
||||
assert user.name == '@username'
|
||||
|
|
Loading…
Add table
Reference in a new issue