mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 14:35:00 +01:00
Api 5.5 (#2809)
Co-authored-by: poolitzer <github@poolitzer.eu> Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
This commit is contained in:
parent
2f6c4075c8
commit
cb95868c4a
15 changed files with 448 additions and 27 deletions
|
@ -1,5 +1,5 @@
|
|||
..
|
||||
Make user to apply any changes to this file to README_RAW.rst as well!
|
||||
Make sure to apply any changes to this file to README_RAW.rst as well!
|
||||
|
||||
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-logo-text_768.png?raw=true
|
||||
:align: center
|
||||
|
@ -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-5.4-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-5.5-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
|
@ -111,7 +111,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 **5.4** are supported.
|
||||
All types and methods of the Telegram Bot API **5.5** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
..
|
||||
Make user to apply any changes to this file to README.rst as well!
|
||||
Make sure to apply any changes to this file to README.rst as well!
|
||||
|
||||
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-raw-logo-text_768.png?raw=true
|
||||
:align: center
|
||||
|
@ -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-5.4-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-5.5-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 **5.4** are supported.
|
||||
All types and methods of the Telegram Bot API **5.5** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
|
@ -579,6 +579,14 @@ class Bot(TelegramObject):
|
|||
) -> Message:
|
||||
"""Use this method to forward messages of any kind. Service messages can't be forwarded.
|
||||
|
||||
Note:
|
||||
Since the release of Bot API 5.5 it can be impossible to forward messages from
|
||||
some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and
|
||||
:attr:`telegram.Chat.has_protected_content` to check this.
|
||||
|
||||
As a workaround, it is still possible to use :meth:`copy_message`. However, this
|
||||
behaviour is undocumented and might be changed by Telegram.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||
of the target channel (in the format ``@channelusername``).
|
||||
|
@ -2408,6 +2416,45 @@ class Bot(TelegramObject):
|
|||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
def ban_chat_sender_chat(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
sender_chat_id: int,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to ban a channel chat in a supergroup or a channel. Until the chat is
|
||||
unbanned, the owner of the banned chat won't be able to send messages on behalf of **any of
|
||||
their channels**. The bot must be an administrator in the supergroup or channel for this
|
||||
to work and must have the appropriate administrator rights.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target group or username
|
||||
of the target supergroup or channel (in the format ``@channelusername``).
|
||||
sender_chat_id (:obj:`int`): Unique identifier of the target sender chat.
|
||||
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:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
|
||||
"""
|
||||
data: JSONDict = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id}
|
||||
|
||||
result = self._post('banChatSenderChat', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
def unban_chat_member(
|
||||
self,
|
||||
|
@ -2437,7 +2484,7 @@ class Bot(TelegramObject):
|
|||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:obj:`bool` On success, :obj:`True` is returned.
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
|
@ -2452,6 +2499,43 @@ class Bot(TelegramObject):
|
|||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
def unban_chat_sender_chat(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
sender_chat_id: int,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Use this method to unban a previously banned channel in a supergroup or channel.
|
||||
The bot must be an administrator for this to work and must have the
|
||||
appropriate administrator rights.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||
of the target supergroup or channel (in the format ``@channelusername``).
|
||||
sender_chat_id (:obj:`int`): Unique identifier of the target sender chat.
|
||||
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:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
|
||||
"""
|
||||
data: JSONDict = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id}
|
||||
|
||||
result = self._post('unbanChatSenderChat', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
def answer_callback_query(
|
||||
self,
|
||||
|
@ -5499,10 +5583,14 @@ class Bot(TelegramObject):
|
|||
"""Alias for :meth:`get_file`"""
|
||||
banChatMember = ban_chat_member
|
||||
"""Alias for :meth:`ban_chat_member`"""
|
||||
banChatSenderChat = ban_chat_sender_chat
|
||||
"""Alias for :meth:`ban_chat_sender_chat`"""
|
||||
kickChatMember = kick_chat_member
|
||||
"""Alias for :meth:`kick_chat_member`"""
|
||||
unbanChatMember = unban_chat_member
|
||||
"""Alias for :meth:`unban_chat_member`"""
|
||||
unbanChatSenderChat = unban_chat_sender_chat
|
||||
"""Alias for :meth:`unban_chat_sender_chat`"""
|
||||
answerCallbackQuery = answer_callback_query
|
||||
"""Alias for :meth:`answer_callback_query`"""
|
||||
editMessageText = edit_message_text
|
||||
|
|
116
telegram/chat.py
116
telegram/chat.py
|
@ -81,6 +81,11 @@ class Chat(TelegramObject):
|
|||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
bio (:obj:`str`, optional): Bio of the other party in a private chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
has_private_forwards (:obj:`bool`, optional): :obj:`True`, if privacy settings of the other
|
||||
party in the private chat allows to use ``tg://user?id=<user_id>`` links only in chats
|
||||
with the user. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
description (:obj:`str`, optional): Description, for groups, supergroups and channel chats.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and
|
||||
|
@ -97,6 +102,10 @@ class Chat(TelegramObject):
|
|||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
has_protected_content (:obj:`bool`, optional): :obj:`True`, if messages from the chat can't
|
||||
be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
@ -119,6 +128,11 @@ class Chat(TelegramObject):
|
|||
photo (:class:`telegram.ChatPhoto`): Optional. Chat photo.
|
||||
bio (:obj:`str`): Optional. Bio of the other party in a private chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
has_private_forwards (:obj:`bool`): Optional. :obj:`True`, if privacy settings of the other
|
||||
party in the private chat allows to use ``tg://user?id=<user_id>`` links only in chats
|
||||
with the user.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats.
|
||||
invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and
|
||||
channel. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
@ -134,6 +148,10 @@ class Chat(TelegramObject):
|
|||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
has_protected_content (:obj:`bool`): Optional. :obj:`True`, if messages from the chat can't
|
||||
be forwarded to other chats.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set.
|
||||
can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the
|
||||
sticker set.
|
||||
|
@ -166,6 +184,8 @@ class Chat(TelegramObject):
|
|||
'linked_chat_id',
|
||||
'all_members_are_administrators',
|
||||
'message_auto_delete_time',
|
||||
'has_protected_content',
|
||||
'has_private_forwards',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
|
@ -204,6 +224,8 @@ class Chat(TelegramObject):
|
|||
linked_chat_id: int = None,
|
||||
location: ChatLocation = None,
|
||||
message_auto_delete_time: int = None,
|
||||
has_private_forwards: bool = None,
|
||||
has_protected_content: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
|
@ -218,6 +240,7 @@ class Chat(TelegramObject):
|
|||
self.all_members_are_administrators = _kwargs.get('all_members_are_administrators')
|
||||
self.photo = photo
|
||||
self.bio = bio
|
||||
self.has_private_forwards = has_private_forwards
|
||||
self.description = description
|
||||
self.invite_link = invite_link
|
||||
self.pinned_message = pinned_message
|
||||
|
@ -226,6 +249,7 @@ class Chat(TelegramObject):
|
|||
self.message_auto_delete_time = (
|
||||
int(message_auto_delete_time) if message_auto_delete_time is not None else None
|
||||
)
|
||||
self.has_protected_content = has_protected_content
|
||||
self.sticker_set_name = sticker_set_name
|
||||
self.can_set_sticker_set = can_set_sticker_set
|
||||
self.linked_chat_id = linked_chat_id
|
||||
|
@ -433,6 +457,98 @@ class Chat(TelegramObject):
|
|||
revoke_messages=revoke_messages,
|
||||
)
|
||||
|
||||
def ban_sender_chat(
|
||||
self,
|
||||
sender_chat_id: int,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.ban_chat_sender_chat(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.ban_chat_sender_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
"""
|
||||
return self.bot.ban_chat_sender_chat(
|
||||
chat_id=self.id, sender_chat_id=sender_chat_id, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
def ban_chat(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.ban_chat_sender_chat(sender_chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.ban_chat_sender_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
"""
|
||||
return self.bot.ban_chat_sender_chat(
|
||||
chat_id=chat_id, sender_chat_id=self.id, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
def unban_sender_chat(
|
||||
self,
|
||||
sender_chat_id: int,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.unban_chat_sender_chat(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.unban_chat_sender_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
"""
|
||||
return self.bot.unban_chat_sender_chat(
|
||||
chat_id=self.id, sender_chat_id=sender_chat_id, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
def unban_chat(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.unban_chat_sender_chat(sender_chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.unban_chat_sender_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
"""
|
||||
return self.bot.unban_chat_sender_chat(
|
||||
chat_id=chat_id, sender_chat_id=self.id, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
def unban_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
|
|
|
@ -34,6 +34,12 @@ class ChatJoinRequest(TelegramObject):
|
|||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`chat`, :attr:`from_user` and :attr:`date` are equal.
|
||||
|
||||
Note:
|
||||
Since Bot API 5.5, bots are allowed to contact users who sent a join request to a chat
|
||||
where the bot is an administrator with the
|
||||
:attr:`~telegram.ChatMemberAdministrator.can_invite_users` administrator right – even if
|
||||
the user never interacted with the bot before.
|
||||
|
||||
.. versionadded:: 13.8
|
||||
|
||||
Args:
|
||||
|
|
|
@ -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`): `5.3`. Telegram Bot API version supported by this
|
||||
BOT_API_VERSION (:obj:`str`): `5.5`. Telegram Bot API version supported by this
|
||||
version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
@ -48,6 +48,10 @@ Attributes:
|
|||
ANONYMOUS_ADMIN_ID (:obj:`int`): ``1087968824`` (User id in groups for anonymous admin)
|
||||
SERVICE_CHAT_ID (:obj:`int`): ``777000`` (Telegram service chat, that also acts as sender of
|
||||
channel posts forwarded to discussion groups)
|
||||
FAKE_CHANNEL_ID (:obj:`int`): ``136817688`` (User id in groups when message is sent on behalf
|
||||
of a channel).
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
The following constants are related to specific classes and are also available
|
||||
as attributes of those classes:
|
||||
|
@ -240,11 +244,12 @@ Attributes:
|
|||
"""
|
||||
from typing import List
|
||||
|
||||
BOT_API_VERSION: str = '5.4'
|
||||
BOT_API_VERSION: str = '5.5'
|
||||
MAX_MESSAGE_LENGTH: int = 4096
|
||||
MAX_CAPTION_LENGTH: int = 1024
|
||||
ANONYMOUS_ADMIN_ID: int = 1087968824
|
||||
SERVICE_CHAT_ID: int = 777000
|
||||
FAKE_CHANNEL_ID: int = 136817688
|
||||
|
||||
# constants above this line are tested
|
||||
|
||||
|
|
|
@ -1965,16 +1965,16 @@ officedocument.wordprocessingml.document")``.
|
|||
|
||||
class sender_chat(_ChatUserBaseFilter):
|
||||
# pylint: disable=W0235
|
||||
"""Filters messages to allow only those which are from a specified sender chats chat ID or
|
||||
"""Filters messages to allow only those which are from a specified sender chat's chat ID or
|
||||
username.
|
||||
|
||||
Examples:
|
||||
* To filter for messages forwarded to a discussion group from a channel with ID
|
||||
* To filter for messages sent to a group by a channel with ID
|
||||
``-1234``, use ``MessageHandler(Filters.sender_chat(-1234), callback_method)``.
|
||||
* To filter for messages of anonymous admins in a super group with username
|
||||
``@anonymous``, use
|
||||
``MessageHandler(Filters.sender_chat(username='anonymous'), callback_method)``.
|
||||
* To filter for messages forwarded to a discussion group from *any* channel, use
|
||||
* To filter for messages sent to a group by *any* channel, use
|
||||
``MessageHandler(Filters.sender_chat.channel, callback_method)``.
|
||||
* To filter for messages of anonymous admins in *any* super group, use
|
||||
``MessageHandler(Filters.sender_chat.super_group, callback_method)``.
|
||||
|
@ -1983,7 +1983,10 @@ officedocument.wordprocessingml.document")``.
|
|||
Remember, ``sender_chat`` is also set for messages in a channel as the channel itself,
|
||||
so when your bot is an admin in a channel and the linked discussion group, you would
|
||||
receive the message twice (once from inside the channel, once inside the discussion
|
||||
group).
|
||||
group). Since v13.9, the field :attr:`telegram.Message.is_automatic_forward` will be
|
||||
:obj:`True` for the discussion group message.
|
||||
|
||||
.. seealso:: :attr:`Filters.is_automatic_forward`
|
||||
|
||||
Warning:
|
||||
:attr:`chat_ids` will return a *copy* of the saved chat ids as :class:`frozenset`. This
|
||||
|
@ -2089,6 +2092,32 @@ officedocument.wordprocessingml.document")``.
|
|||
super_group = _SuperGroup()
|
||||
channel = _Channel()
|
||||
|
||||
class _IsAutomaticForward(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.is_automatic_forward'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.is_automatic_forward)
|
||||
|
||||
is_automatic_forward = _IsAutomaticForward()
|
||||
"""Messages that contain :attr:`telegram.Message.is_automatic_forward`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
"""
|
||||
|
||||
class _HasProtectedContent(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.has_protected_content'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.has_protected_content)
|
||||
|
||||
has_protected_content = _HasProtectedContent()
|
||||
"""Messages that contain :attr:`telegram.Message.has_protected_content`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
"""
|
||||
|
||||
class _Invoice(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.invoice'
|
||||
|
|
|
@ -45,16 +45,25 @@ class InlineKeyboardButton(TelegramObject):
|
|||
|
||||
.. versionadded:: 13.6
|
||||
|
||||
* Since Bot API 5.5, it's now allowed to mention users by their ID in inline keyboards.
|
||||
This will only work in Telegram versions released after December 7, 2021.
|
||||
Older clients will display *unsupported message*.
|
||||
|
||||
Warning:
|
||||
If your bot allows your arbitrary callback data, buttons whose callback data is a
|
||||
non-hashable object will be come unhashable. Trying to evaluate ``hash(button)`` will
|
||||
non-hashable object will become unhashable. Trying to evaluate ``hash(button)`` will
|
||||
result in a :class:`TypeError`.
|
||||
|
||||
.. versionchanged:: 13.6
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
url (:obj:`str`, optional): HTTP or tg:// url to be opened when button is pressed.
|
||||
url (:obj:`str`, optional): HTTP or tg:// url to be opened when the button is pressed.
|
||||
Links ``tg://user?id=<user_id>`` can be used to mention a user by
|
||||
their ID without using a username, if this is allowed by their privacy settings.
|
||||
|
||||
.. versionchanged:: 13.9
|
||||
You can now mention a user using ``tg://user?id=<user_id>``.
|
||||
login_url (:class:`telegram.LoginUrl`, optional): An HTTP URL used to automatically
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
callback_data (:obj:`str` | :obj:`Any`, optional): Data to be sent in a callback query to
|
||||
|
@ -76,12 +85,18 @@ class InlineKeyboardButton(TelegramObject):
|
|||
be launched when the user presses the button. This type of button must always be
|
||||
the ``first`` button in the first row.
|
||||
pay (:obj:`bool`, optional): Specify :obj:`True`, to send a Pay button. This type of button
|
||||
must always be the ``first`` button in the first row.
|
||||
must always be the `first` button in the first row and can only be used in invoice
|
||||
messages.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
url (:obj:`str`): Optional. HTTP or tg:// url to be opened when button is pressed.
|
||||
url (:obj:`str`): Optional. HTTP or tg:// url to be opened when the button is pressed.
|
||||
Links ``tg://user?id=<user_id>`` can be used to mention a user by
|
||||
their ID without using a username, if this is allowed by their privacy settings.
|
||||
|
||||
.. versionchanged:: 13.9
|
||||
You can now mention a user using ``tg://user?id=<user_id>``.
|
||||
login_url (:class:`telegram.LoginUrl`): Optional. An HTTP URL used to automatically
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
callback_data (:obj:`str` | :obj:`object`): Optional. Data to be sent in a callback query
|
||||
|
|
|
@ -90,12 +90,15 @@ class Message(TelegramObject):
|
|||
|
||||
Args:
|
||||
message_id (:obj:`int`): Unique message identifier inside this chat.
|
||||
from_user (:class:`telegram.User`, optional): Sender, empty for messages sent
|
||||
to channels.
|
||||
from_user (:class:`telegram.User`, optional): Sender of the message; empty for messages
|
||||
sent to channels. For backward compatibility, this will contain a fake sender user in
|
||||
non-channel chats, if the message was sent on behalf of a chat.
|
||||
sender_chat (:class:`telegram.Chat`, optional): Sender of the message, sent on behalf of a
|
||||
chat. The channel itself for channel messages. The supergroup itself for messages from
|
||||
anonymous group administrators. The linked channel for messages automatically forwarded
|
||||
to the discussion group.
|
||||
chat. For example, the channel itself for channel posts, the supergroup itself for
|
||||
messages from anonymous group administrators, the linked channel for messages
|
||||
automatically forwarded to the discussion group. For backward compatibility,
|
||||
:attr:`from_user` contains a fake sender user in non-channel chats, if the message was
|
||||
sent on behalf of a chat.
|
||||
date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to
|
||||
:class:`datetime.datetime`.
|
||||
chat (:class:`telegram.Chat`): Conversation the message belongs to.
|
||||
|
@ -109,9 +112,17 @@ class Message(TelegramObject):
|
|||
who disallow adding a link to their account in forwarded messages.
|
||||
forward_date (:class:`datetime.datetime`, optional): For forwarded messages, date the
|
||||
original message was sent in Unix time. Converted to :class:`datetime.datetime`.
|
||||
is_automatic_forward (:obj:`bool`, optional): :obj:`True`, if the message is a channel post
|
||||
that was automatically forwarded to the connected discussion group.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
reply_to_message (:class:`telegram.Message`, optional): For replies, the original message.
|
||||
edit_date (:class:`datetime.datetime`, optional): Date the message was last edited in Unix
|
||||
time. Converted to :class:`datetime.datetime`.
|
||||
has_protected_content (:obj:`bool`, optional): :obj:`True`, if the message can't be
|
||||
forwarded.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
media_group_id (:obj:`str`, optional): The unique identifier of a media message group this
|
||||
message belongs to.
|
||||
text (str, optional): For text messages, the actual UTF-8 text of the message, 0-4096
|
||||
|
@ -225,11 +236,12 @@ class Message(TelegramObject):
|
|||
|
||||
Attributes:
|
||||
message_id (:obj:`int`): Unique message identifier inside this chat.
|
||||
from_user (:class:`telegram.User`): Optional. Sender.
|
||||
from_user (:class:`telegram.User`): Optional. Sender of the message; empty for messages
|
||||
sent to channels. For backward compatibility, this will contain a fake sender user in
|
||||
non-channel chats, if the message was sent on behalf of a chat.
|
||||
sender_chat (:class:`telegram.Chat`): Optional. Sender of the message, sent on behalf of a
|
||||
chat. The channel itself for channel messages. The supergroup itself for messages from
|
||||
anonymous group administrators. The linked channel for messages automatically forwarded
|
||||
to the discussion group.
|
||||
chat. For backward compatibility, :attr:`from_user` contains a fake sender user in
|
||||
non-channel chats, if the message was sent on behalf of a chat.
|
||||
date (:class:`datetime.datetime`): Date the message was sent.
|
||||
chat (:class:`telegram.Chat`): Conversation the message belongs to.
|
||||
forward_from (:class:`telegram.User`): Optional. Sender of the original message.
|
||||
|
@ -238,10 +250,18 @@ class Message(TelegramObject):
|
|||
forward_from_message_id (:obj:`int`): Optional. Identifier of the original message in the
|
||||
channel.
|
||||
forward_date (:class:`datetime.datetime`): Optional. Date the original message was sent.
|
||||
is_automatic_forward (:obj:`bool`): Optional. :obj:`True`, if the message is a channel post
|
||||
that was automatically forwarded to the connected discussion group.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
reply_to_message (:class:`telegram.Message`): Optional. For replies, the original message.
|
||||
Note that the Message object in this field will not contain further
|
||||
``reply_to_message`` fields even if it itself is a reply.
|
||||
edit_date (:class:`datetime.datetime`): Optional. Date the message was last edited.
|
||||
has_protected_content (:obj:`bool`): Optional. :obj:`True`, if the message can't be
|
||||
forwarded.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
media_group_id (:obj:`str`): Optional. The unique identifier of a media message group this
|
||||
message belongs to.
|
||||
text (:obj:`str`): Optional. The actual UTF-8 text of the message.
|
||||
|
@ -390,6 +410,8 @@ class Message(TelegramObject):
|
|||
'voice_chat_participants_invited',
|
||||
'voice_chat_started',
|
||||
'voice_chat_scheduled',
|
||||
'is_automatic_forward',
|
||||
'has_protected_content',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
|
@ -492,6 +514,8 @@ class Message(TelegramObject):
|
|||
voice_chat_participants_invited: VoiceChatParticipantsInvited = None,
|
||||
message_auto_delete_timer_changed: MessageAutoDeleteTimerChanged = None,
|
||||
voice_chat_scheduled: VoiceChatScheduled = None,
|
||||
is_automatic_forward: bool = None,
|
||||
has_protected_content: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
|
@ -504,8 +528,10 @@ class Message(TelegramObject):
|
|||
self.forward_from = forward_from
|
||||
self.forward_from_chat = forward_from_chat
|
||||
self.forward_date = forward_date
|
||||
self.is_automatic_forward = is_automatic_forward
|
||||
self.reply_to_message = reply_to_message
|
||||
self.edit_date = edit_date
|
||||
self.has_protected_content = has_protected_content
|
||||
self.text = text
|
||||
self.entities = entities or []
|
||||
self.caption_entities = caption_entities or []
|
||||
|
@ -1795,6 +1821,14 @@ class Message(TelegramObject):
|
|||
|
||||
For the documentation of the arguments, please see :meth:`telegram.Bot.forward_message`.
|
||||
|
||||
Note:
|
||||
Since the release of Bot API 5.5 it can be impossible to forward messages from
|
||||
some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and
|
||||
:attr:`telegram.Chat.has_protected_content` to check this.
|
||||
|
||||
As a workaround, it is still possible to use :meth:`copy`. However, this
|
||||
behaviour is undocumented and might be changed by Telegram.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, instance representing the message forwarded.
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ from datetime import datetime
|
|||
from typing import TYPE_CHECKING, Any, List, Optional, Union, Tuple
|
||||
|
||||
from telegram import TelegramObject, constants
|
||||
from telegram.inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from telegram.utils.helpers import (
|
||||
mention_html as util_mention_html,
|
||||
DEFAULT_NONE,
|
||||
|
@ -233,6 +234,22 @@ class User(TelegramObject):
|
|||
return util_mention_html(self.id, name)
|
||||
return util_mention_html(self.id, self.full_name)
|
||||
|
||||
def mention_button(self, name: str = None) -> InlineKeyboardButton:
|
||||
"""
|
||||
Shortcut for::
|
||||
|
||||
InlineKeyboardButton(text=name, url=f"tg://user?id={update.effective_user.id}")
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
Args:
|
||||
name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.InlineKeyboardButton`: InlineButton with url set to the user mention
|
||||
"""
|
||||
return InlineKeyboardButton(text=name or self.full_name, url=f"tg://user?id={self.id}")
|
||||
|
||||
def pin_message(
|
||||
self,
|
||||
message_id: int,
|
||||
|
|
|
@ -1012,6 +1012,17 @@ class TestBot:
|
|||
assert tz_bot.ban_chat_member(2, 32, until_date=until)
|
||||
assert tz_bot.ban_chat_member(2, 32, until_date=until_timestamp)
|
||||
|
||||
def test_ban_chat_sender_chat(self, monkeypatch, bot):
|
||||
# For now, we just test that we pass the correct data to TG
|
||||
def make_assertion(url, data, *args, **kwargs):
|
||||
chat_id = data['chat_id'] == 2
|
||||
sender_chat_id = data['sender_chat_id'] == 32
|
||||
return chat_id and sender_chat_id
|
||||
|
||||
monkeypatch.setattr(bot.request, 'post', make_assertion)
|
||||
assert bot.ban_chat_sender_chat(2, 32)
|
||||
monkeypatch.delattr(bot.request, 'post')
|
||||
|
||||
def test_kick_chat_member_warning(self, monkeypatch, bot, recwarn):
|
||||
def test(url, data, *args, **kwargs):
|
||||
chat_id = data['chat_id'] == 2
|
||||
|
@ -1037,6 +1048,15 @@ class TestBot:
|
|||
|
||||
assert bot.unban_chat_member(2, 32, only_if_banned=only_if_banned)
|
||||
|
||||
def test_unban_chat_sender_chat(self, monkeypatch, bot):
|
||||
def make_assertion(url, data, *args, **kwargs):
|
||||
chat_id = data['chat_id'] == 2
|
||||
sender_chat_id = data['sender_chat_id'] == 32
|
||||
return chat_id and sender_chat_id
|
||||
|
||||
monkeypatch.setattr(bot.request, 'post', make_assertion)
|
||||
assert bot.unbanChatSenderChat(2, 32)
|
||||
|
||||
def test_set_chat_permissions(self, monkeypatch, bot, chat_permissions):
|
||||
def test(url, data, *args, **kwargs):
|
||||
chat_id = data['chat_id'] == 2
|
||||
|
|
|
@ -41,6 +41,8 @@ def chat(bot):
|
|||
bio=TestChat.bio,
|
||||
linked_chat_id=TestChat.linked_chat_id,
|
||||
location=TestChat.location,
|
||||
has_private_forwards=True,
|
||||
has_protected_content=True,
|
||||
)
|
||||
|
||||
|
||||
|
@ -62,6 +64,8 @@ class TestChat:
|
|||
bio = "I'm a Barbie Girl in a Barbie World"
|
||||
linked_chat_id = 11880
|
||||
location = ChatLocation(Location(123, 456), 'Barbie World')
|
||||
has_protected_content = True
|
||||
has_private_forwards = True
|
||||
|
||||
def test_slot_behaviour(self, chat, recwarn, mro_slots):
|
||||
for attr in chat.__slots__:
|
||||
|
@ -84,6 +88,8 @@ class TestChat:
|
|||
'slow_mode_delay': self.slow_mode_delay,
|
||||
'message_auto_delete_time': self.message_auto_delete_time,
|
||||
'bio': self.bio,
|
||||
'has_protected_content': self.has_protected_content,
|
||||
'has_private_forwards': self.has_private_forwards,
|
||||
'linked_chat_id': self.linked_chat_id,
|
||||
'location': self.location.to_dict(),
|
||||
}
|
||||
|
@ -100,6 +106,8 @@ class TestChat:
|
|||
assert chat.slow_mode_delay == self.slow_mode_delay
|
||||
assert chat.message_auto_delete_time == self.message_auto_delete_time
|
||||
assert chat.bio == self.bio
|
||||
assert chat.has_protected_content == self.has_protected_content
|
||||
assert chat.has_private_forwards == self.has_private_forwards
|
||||
assert chat.linked_chat_id == self.linked_chat_id
|
||||
assert chat.location.location == self.location.location
|
||||
assert chat.location.address == self.location.address
|
||||
|
@ -117,6 +125,8 @@ class TestChat:
|
|||
assert chat_dict['slow_mode_delay'] == chat.slow_mode_delay
|
||||
assert chat_dict['message_auto_delete_time'] == chat.message_auto_delete_time
|
||||
assert chat_dict['bio'] == chat.bio
|
||||
assert chat_dict['has_private_forwards'] == chat.has_private_forwards
|
||||
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()
|
||||
|
||||
|
@ -225,6 +235,36 @@ class TestChat:
|
|||
monkeypatch.setattr(chat.bot, 'ban_chat_member', make_assertion)
|
||||
assert chat.ban_member(user_id=42, until_date=43)
|
||||
|
||||
def test_ban_sender_chat(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == chat.id
|
||||
sender_chat_id = kwargs['sender_chat_id'] == 42
|
||||
return chat_id and sender_chat_id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.ban_sender_chat, Bot.ban_chat_sender_chat, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.ban_sender_chat, chat.bot, 'ban_chat_sender_chat')
|
||||
assert check_defaults_handling(chat.ban_sender_chat, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'ban_chat_sender_chat', make_assertion)
|
||||
assert chat.ban_sender_chat(42)
|
||||
|
||||
def test_ban_chat(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == 42
|
||||
sender_chat_id = kwargs['sender_chat_id'] == chat.id
|
||||
return chat_id and sender_chat_id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.ban_chat, Bot.ban_chat_sender_chat, ['sender_chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.ban_chat, chat.bot, 'ban_chat_sender_chat')
|
||||
assert check_defaults_handling(chat.ban_chat, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'ban_chat_sender_chat', make_assertion)
|
||||
assert chat.ban_chat(42)
|
||||
|
||||
def test_kick_member_warning(self, chat, monkeypatch, recwarn):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == chat.id
|
||||
|
@ -252,6 +292,36 @@ class TestChat:
|
|||
monkeypatch.setattr(chat.bot, 'unban_chat_member', make_assertion)
|
||||
assert chat.unban_member(user_id=42, only_if_banned=only_if_banned)
|
||||
|
||||
def test_unban_sender_chat(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == chat.id
|
||||
sender_chat_id = kwargs['sender_chat_id'] == 42
|
||||
return chat_id and sender_chat_id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.unban_sender_chat, Bot.unban_chat_sender_chat, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.unban_sender_chat, chat.bot, 'unban_chat_sender_chat')
|
||||
assert check_defaults_handling(chat.unban_sender_chat, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'unban_chat_sender_chat', make_assertion)
|
||||
assert chat.unban_sender_chat(42)
|
||||
|
||||
def test_unban_chat(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == 42
|
||||
sender_chat_id = kwargs['sender_chat_id'] == chat.id
|
||||
return chat_id and sender_chat_id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.unban_chat, Bot.ban_chat_sender_chat, ['sender_chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.unban_chat, chat.bot, 'unban_chat_sender_chat')
|
||||
assert check_defaults_handling(chat.unban_chat, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'unban_chat_sender_chat', make_assertion)
|
||||
assert chat.unban_chat(42)
|
||||
|
||||
@pytest.mark.parametrize('is_anonymous', [True, False, None])
|
||||
def test_promote_member(self, monkeypatch, chat, is_anonymous):
|
||||
def make_assertion(*_, **kwargs):
|
||||
|
|
|
@ -1718,6 +1718,16 @@ class TestFilters:
|
|||
update.message.sender_chat = None
|
||||
assert not Filters.sender_chat.channel(update)
|
||||
|
||||
def test_filters_is_automatic_forward(self, update):
|
||||
assert not Filters.is_automatic_forward(update)
|
||||
update.message.is_automatic_forward = True
|
||||
assert Filters.is_automatic_forward(update)
|
||||
|
||||
def test_filters_has_protected_content(self, update):
|
||||
assert not Filters.has_protected_content(update)
|
||||
update.message.has_protected_content = True
|
||||
assert Filters.has_protected_content(update)
|
||||
|
||||
def test_filters_invoice(self, update):
|
||||
assert not Filters.invoice(update)
|
||||
update.message.invoice = 'test'
|
||||
|
|
|
@ -180,6 +180,8 @@ def message(bot):
|
|||
)
|
||||
},
|
||||
{'sender_chat': Chat(-123, 'discussion_channel')},
|
||||
{'is_automatic_forward': True},
|
||||
{'has_protected_content': True},
|
||||
],
|
||||
ids=[
|
||||
'forwarded_user',
|
||||
|
@ -229,6 +231,8 @@ def message(bot):
|
|||
'voice_chat_ended',
|
||||
'voice_chat_participants_invited',
|
||||
'sender_chat',
|
||||
'is_automatic_forward',
|
||||
'has_protected_content',
|
||||
],
|
||||
)
|
||||
def message_params(bot, request):
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import pytest
|
||||
|
||||
from telegram import Update, User, Bot
|
||||
from telegram import Update, User, Bot, InlineKeyboardButton
|
||||
from telegram.utils.helpers import escape_markdown
|
||||
from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling
|
||||
|
||||
|
@ -473,6 +473,13 @@ class TestUser:
|
|||
)
|
||||
assert user.mention_html(user.username) == expected.format(user.id, user.username)
|
||||
|
||||
def test_mention_button(self, user):
|
||||
expected_name = InlineKeyboardButton(text="Bob", url=f"tg://user?id={user.id}")
|
||||
expected_full = InlineKeyboardButton(text=user.full_name, url=f"tg://user?id={user.id}")
|
||||
|
||||
assert user.mention_button("Bob") == expected_name
|
||||
assert user.mention_button() == expected_full
|
||||
|
||||
def test_mention_markdown(self, user):
|
||||
expected = '[{}](tg://user?id={})'
|
||||
|
||||
|
|
Loading…
Reference in a new issue