Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
This commit is contained in:
Poolitzer 2023-02-08 11:47:21 +01:00 committed by GitHub
parent 9953216980
commit 23a685335b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 1228 additions and 35 deletions

View file

@ -14,7 +14,7 @@
:target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-6.4-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-6.5-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions
@ -93,7 +93,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.4** are supported.
All types and methods of the Telegram Bot API **6.5** are supported.
Installing
==========

View file

@ -14,7 +14,7 @@
:target: https://pypi.org/project/python-telegram-bot-raw/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-6.4-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-6.5-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions
@ -89,7 +89,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.4** are supported.
All types and methods of the Telegram Bot API **6.5** are supported.
Installing
==========

View file

@ -31,6 +31,7 @@ Available Types
telegram.chatmemberupdated
telegram.chatpermissions
telegram.chatphoto
telegram.chatshared
telegram.contact
telegram.dice
telegram.document
@ -54,6 +55,8 @@ Available Types
telegram.inputmediavideo
telegram.keyboardbutton
telegram.keyboardbuttonpolltype
telegram.keyboardbuttonrequestchat
telegram.keyboardbuttonrequestuser
telegram.location
telegram.loginurl
telegram.menubutton
@ -76,6 +79,7 @@ Available Types
telegram.update
telegram.user
telegram.userprofilephotos
telegram.usershared
telegram.venue
telegram.video
telegram.videochatended

View file

@ -0,0 +1,6 @@
ChatShared
===================
.. autoclass:: telegram.ChatShared
:members:
:show-inheritance:

View file

@ -0,0 +1,6 @@
KeyboardButtonRequestChat
==================================
.. autoclass:: telegram.KeyboardButtonRequestChat
:members:
:show-inheritance:

View file

@ -0,0 +1,6 @@
KeyboardButtonRequestUser
==================================
.. autoclass:: telegram.KeyboardButtonRequestUser
:members:
:show-inheritance:

View file

@ -0,0 +1,6 @@
UserShared
===================
.. autoclass:: telegram.UserShared
:members:
:show-inheritance:

View file

@ -54,6 +54,7 @@ __all__ = ( # Keep this alphabetically ordered
"ChatMemberUpdated",
"ChatPermissions",
"ChatPhoto",
"ChatShared",
"ChosenInlineResult",
"constants",
"Contact",
@ -118,6 +119,8 @@ __all__ = ( # Keep this alphabetically ordered
"Invoice",
"KeyboardButton",
"KeyboardButtonPollType",
"KeyboardButtonRequestChat",
"KeyboardButtonRequestUser",
"LabeledPrice",
"Location",
"LoginUrl",
@ -167,6 +170,7 @@ __all__ = ( # Keep this alphabetically ordered
"Update",
"User",
"UserProfilePhotos",
"UserShared",
"Venue",
"Video",
"VideoChatEnded",
@ -282,6 +286,7 @@ from ._inline.inputtextmessagecontent import InputTextMessageContent
from ._inline.inputvenuemessagecontent import InputVenueMessageContent
from ._keyboardbutton import KeyboardButton
from ._keyboardbuttonpolltype import KeyboardButtonPollType
from ._keyboardbuttonrequest import KeyboardButtonRequestChat, KeyboardButtonRequestUser
from ._loginurl import LoginUrl
from ._menubutton import MenuButton, MenuButtonCommands, MenuButtonDefault, MenuButtonWebApp
from ._message import Message
@ -325,6 +330,7 @@ from ._proximityalerttriggered import ProximityAlertTriggered
from ._replykeyboardmarkup import ReplyKeyboardMarkup
from ._replykeyboardremove import ReplyKeyboardRemove
from ._sentwebappmessage import SentWebAppMessage
from ._shared import ChatShared, UserShared
from ._telegramobject import TelegramObject
from ._update import Update
from ._user import User

View file

@ -4467,6 +4467,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
user_id: Union[str, int],
permissions: ChatPermissions,
until_date: Union[int, datetime] = None,
use_independent_chat_permissions: bool = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@ -4493,6 +4494,21 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
used.
permissions (:class:`telegram.ChatPermissions`): An object for new user
permissions.
use_independent_chat_permissions (:obj:`bool`, optional): Pass :obj:`True` if chat
permissions are set independently. Otherwise, the
:attr:`~telegram.ChatPermissions.can_send_other_messages` and
:attr:`~telegram.ChatPermissions.can_add_web_page_previews` permissions will imply
the :attr:`~telegram.ChatPermissions.can_send_messages`,
:attr:`~telegram.ChatPermissions.can_send_audios`,
:attr:`~telegram.ChatPermissions.can_send_documents`,
:attr:`~telegram.ChatPermissions.can_send_photos`,
:attr:`~telegram.ChatPermissions.can_send_videos`,
:attr:`~telegram.ChatPermissions.can_send_video_notes`, and
:attr:`~telegram.ChatPermissions.can_send_voice_notes` permissions; the
:attr:`~telegram.ChatPermissions.can_send_polls` permission will imply the
:attr:`~telegram.ChatPermissions.can_send_messages` permission.
.. versionadded: 20.1
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@ -4505,6 +4521,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
"user_id": user_id,
"permissions": permissions,
"until_date": until_date,
"use_independent_chat_permissions": use_independent_chat_permissions,
}
result = await self._post(
@ -4633,6 +4650,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
self,
chat_id: Union[str, int],
permissions: ChatPermissions,
use_independent_chat_permissions: bool = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@ -4648,6 +4666,21 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
permissions (:class:`telegram.ChatPermissions`): New default chat permissions.
use_independent_chat_permissions (:obj:`bool`, optional): Pass :obj:`True` if chat
permissions are set independently. Otherwise, the
:attr:`~telegram.ChatPermissions.can_send_other_messages` and
:attr:`~telegram.ChatPermissions.can_add_web_page_previews` permissions will imply
the :attr:`~telegram.ChatPermissions.can_send_messages`,
:attr:`~telegram.ChatPermissions.can_send_audios`,
:attr:`~telegram.ChatPermissions.can_send_documents`,
:attr:`~telegram.ChatPermissions.can_send_photos`,
:attr:`~telegram.ChatPermissions.can_send_videos`,
:attr:`~telegram.ChatPermissions.can_send_video_notes`, and
:attr:`~telegram.ChatPermissions.can_send_voice_notes` permissions; the
:attr:`~telegram.ChatPermissions.can_send_polls` permission will imply the
:attr:`~telegram.ChatPermissions.can_send_messages` permission.
.. versionadded: 20.1
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@ -4656,7 +4689,11 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {"chat_id": chat_id, "permissions": permissions}
data: JSONDict = {
"chat_id": chat_id,
"permissions": permissions,
"use_independent_chat_permissions": use_independent_chat_permissions,
}
result = await self._post(
"setChatPermissions",
data,

View file

@ -943,6 +943,7 @@ class Chat(TelegramObject):
user_id: Union[str, int],
permissions: ChatPermissions,
until_date: Union[int, datetime] = None,
use_independent_chat_permissions: bool = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@ -959,6 +960,9 @@ class Chat(TelegramObject):
.. versionadded:: 13.2
.. versionadded:: 20.1
Added :paramref:`~telegram.Bot.restrict_chat_member.use_independent_chat_permissions`.
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@ -968,6 +972,7 @@ class Chat(TelegramObject):
user_id=user_id,
permissions=permissions,
until_date=until_date,
use_independent_chat_permissions=use_independent_chat_permissions,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
@ -978,6 +983,7 @@ class Chat(TelegramObject):
async def set_permissions(
self,
permissions: ChatPermissions,
use_independent_chat_permissions: bool = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@ -992,6 +998,9 @@ class Chat(TelegramObject):
For the documentation of the arguments, please see
:meth:`telegram.Bot.set_chat_permissions`.
.. versionadded:: 20.1
Added :paramref:`~telegram.Bot.set_chat_permissions.use_independent_chat_permissions`.
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@ -999,6 +1008,7 @@ class Chat(TelegramObject):
return await self.get_bot().set_chat_permissions(
chat_id=self.id,
permissions=permissions,
use_independent_chat_permissions=use_independent_chat_permissions,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,

View file

@ -39,17 +39,32 @@ class ChatJoinRequest(TelegramObject):
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.
* 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.
* Telegram does not guarantee that :attr:`from_user.id <from_user>` coincides with the
``chat_id`` of the user. Please use :attr:`user_chat_id` to contact the user in
response to their join request.
.. versionadded:: 13.8
.. versionchanged:: 20.1
In Bot API 6.5 the argument :paramref:`user_chat_id` was added, which changes the position
of the optional arguments :paramref:`bio` and :paramref:`invite_link`.
Args:
chat (:class:`telegram.Chat`): Chat to which the request was sent.
from_user (:class:`telegram.User`): User that sent the join request.
date (:class:`datetime.datetime`): Date the request was sent.
user_chat_id (:obj:`int`): Identifier of a private chat with the user who sent the join
request. This number may have more than 32 significant bits and some programming
languages may have difficulty/silent defects in interpreting it. But it has at most 52
significant bits, so a 64-bit integer or double-precision float type are safe for
storing this identifier. The bot can use this identifier for 24 hours to send messages
until the join request is processed, assuming no other administrator contacted the
user.
.. versionadded:: 20.1
bio (:obj:`str`, optional): Bio of the user.
invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link that was used
by the user to send the join request.
@ -58,19 +73,29 @@ class ChatJoinRequest(TelegramObject):
chat (:class:`telegram.Chat`): Chat to which the request was sent.
from_user (:class:`telegram.User`): User that sent the join request.
date (:class:`datetime.datetime`): Date the request was sent.
user_chat_id (:obj:`int`): Identifier of a private chat with the user who sent the join
request. This number may have more than 32 significant bits and some programming
languages may have difficulty/silent defects in interpreting it. But it has at most 52
significant bits, so a 64-bit integer or double-precision float type are safe for
storing this identifier. The bot can use this identifier for 24 hours to send messages
until the join request is processed, assuming no other administrator contacted the
user.
.. versionadded:: 20.1
bio (:obj:`str`): Optional. Bio of the user.
invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link that was used
by the user to send the join request.
"""
__slots__ = ("chat", "from_user", "date", "bio", "invite_link")
__slots__ = ("chat", "from_user", "date", "bio", "invite_link", "user_chat_id")
def __init__(
self,
chat: Chat,
from_user: User,
date: datetime.datetime,
user_chat_id: int,
bio: str = None,
invite_link: ChatInviteLink = None,
*,
@ -81,6 +106,7 @@ class ChatJoinRequest(TelegramObject):
self.chat: Chat = chat
self.from_user: User = from_user
self.date: datetime.datetime = date
self.user_chat_id: int = user_chat_id
# Optionals
self.bio: Optional[str] = bio

View file

@ -354,8 +354,8 @@ class ChatMemberRestricted(ChatMember):
.. versionadded:: 13.7
.. versionchanged:: 20.0
The argument :paramref:`can_manage_topics` was added, which changes the position of the
optional argument :paramref:`until_date`.
All arguments were made positional and their order was changed.
The argument can_manage_topics was added.
Args:
user (:class:`telegram.User`): Information about the user.
@ -368,9 +368,12 @@ class ChatMemberRestricted(ChatMember):
can_pin_messages (:obj:`bool`): :obj:`True`, if the user is allowed
to pin messages; groups and supergroups only.
can_send_messages (:obj:`bool`): :obj:`True`, if the user is allowed
to send text messages, contacts, locations and venues.
to send text messages, contacts, invoices, locations and venues.
can_send_media_messages (:obj:`bool`): :obj:`True`, if the user is allowed
to send audios, documents, photos, videos, video notes and voice notes.
.. deprecated:: 20.1
Bot API 6.5 replaced this argument with granular media settings.
can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed
to send polls.
can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed
@ -383,6 +386,26 @@ class ChatMemberRestricted(ChatMember):
.. versionadded:: 20.0
until_date (:class:`datetime.datetime`): Date when restrictions
will be lifted for this user.
can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios.
.. versionadded:: 20.1
can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents.
.. versionadded:: 20.1
can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos.
.. versionadded:: 20.1
can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos.
.. versionadded:: 20.1
can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video
notes.
.. versionadded:: 20.1
can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice
notes.
.. versionadded:: 20.1
Attributes:
status (:obj:`str`): The member's status in the chat,
@ -400,6 +423,9 @@ class ChatMemberRestricted(ChatMember):
to send text messages, contacts, locations and venues.
can_send_media_messages (:obj:`bool`): :obj:`True`, if the user is allowed
to send audios, documents, photos, videos, video notes and voice notes.
.. deprecated:: 20.1
Bot API 6.5 replaced this attribute with granular media settings.
can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed
to send polls.
can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed
@ -412,6 +438,26 @@ class ChatMemberRestricted(ChatMember):
.. versionadded:: 20.0
until_date (:class:`datetime.datetime`): Date when restrictions
will be lifted for this user.
can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios.
.. versionadded:: 20.1
can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents.
.. versionadded:: 20.1
can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos.
.. versionadded:: 20.1
can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos.
.. versionadded:: 20.1
can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video
notes.
.. versionadded:: 20.1
can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice
notes.
.. versionadded:: 20.1
"""
@ -427,6 +473,12 @@ class ChatMemberRestricted(ChatMember):
"can_add_web_page_previews",
"can_manage_topics",
"until_date",
"can_send_audios",
"can_send_documents",
"can_send_photos",
"can_send_videos",
"can_send_video_notes",
"can_send_voice_notes",
)
def __init__(
@ -443,6 +495,12 @@ class ChatMemberRestricted(ChatMember):
can_add_web_page_previews: bool,
can_manage_topics: bool,
until_date: datetime.datetime,
can_send_audios: bool,
can_send_documents: bool,
can_send_photos: bool,
can_send_videos: bool,
can_send_video_notes: bool,
can_send_voice_notes: bool,
*,
api_kwargs: JSONDict = None,
):
@ -459,6 +517,12 @@ class ChatMemberRestricted(ChatMember):
self.can_add_web_page_previews: bool = can_add_web_page_previews
self.can_manage_topics: bool = can_manage_topics
self.until_date: datetime.datetime = until_date
self.can_send_audios: bool = can_send_audios
self.can_send_documents: bool = can_send_documents
self.can_send_photos: bool = can_send_photos
self.can_send_videos: bool = can_send_videos
self.can_send_video_notes: bool = can_send_video_notes
self.can_send_voice_notes: bool = can_send_voice_notes
class ChatMemberLeft(ChatMember):

View file

@ -21,6 +21,8 @@ from typing import Optional
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
from telegram._utils.warnings import warn
from telegram.warnings import PTBDeprecationWarning
class ChatPermissions(TelegramObject):
@ -35,6 +37,11 @@ class ChatPermissions(TelegramObject):
.. versionchanged:: 20.0
:attr:`can_manage_topics` is considered as well when comparing objects of
this type in terms of equality.
.. deprecated:: 20.1
:attr:`can_send_audios`, :attr:`can_send_documents`, :attr:`can_send_photos`,
:attr:`can_send_videos`, :attr:`can_send_video_notes` and :attr:`can_send_voice_notes`
will be considered as well when comparing objects of this type in terms of equality in
V21.
Note:
Though not stated explicitly in the official docs, Telegram changes not only the
@ -47,6 +54,9 @@ class ChatPermissions(TelegramObject):
can_send_media_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to
send audios, documents, photos, videos, video notes and voice notes, implies
:attr:`can_send_messages`.
.. deprecated:: 20.1
Bot API 6.5 replaced this argument with granular media settings.
can_send_polls (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send polls,
implies :attr:`can_send_messages`.
can_send_other_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to
@ -65,6 +75,26 @@ class ChatPermissions(TelegramObject):
:attr:`can_pin_messages`.
.. versionadded:: 20.0
can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios.
.. versionadded:: 20.1
can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents.
.. versionadded:: 20.1
can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos.
.. versionadded:: 20.1
can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos.
.. versionadded:: 20.1
can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video
notes.
.. versionadded:: 20.1
can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice
notes.
.. versionadded:: 20.1
Attributes:
can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send text
@ -72,6 +102,9 @@ class ChatPermissions(TelegramObject):
can_send_media_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to
send audios, documents, photos, videos, video notes and voice notes, implies
:attr:`can_send_messages`.
.. deprecated:: 20.1
Bot API 6.5 replaced this attribute with granular media settings.
can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send polls,
implies :attr:`can_send_messages`.
can_send_other_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to
@ -90,6 +123,26 @@ class ChatPermissions(TelegramObject):
:attr:`can_pin_messages`.
.. versionadded:: 20.0
can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios.
.. versionadded:: 20.1
can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents.
.. versionadded:: 20.1
can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos.
.. versionadded:: 20.1
can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos.
.. versionadded:: 20.1
can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video
notes.
.. versionadded:: 20.1
can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice
notes.
.. versionadded:: 20.1
"""
@ -103,6 +156,12 @@ class ChatPermissions(TelegramObject):
"can_pin_messages",
"can_add_web_page_previews",
"can_manage_topics",
"can_send_audios",
"can_send_documents",
"can_send_photos",
"can_send_videos",
"can_send_video_notes",
"can_send_voice_notes",
)
def __init__(
@ -116,6 +175,12 @@ class ChatPermissions(TelegramObject):
can_invite_users: bool = None,
can_pin_messages: bool = None,
can_manage_topics: bool = None,
can_send_audios: bool = None,
can_send_documents: bool = None,
can_send_photos: bool = None,
can_send_videos: bool = None,
can_send_video_notes: bool = None,
can_send_voice_notes: bool = None,
*,
api_kwargs: JSONDict = None,
):
@ -130,6 +195,12 @@ class ChatPermissions(TelegramObject):
self.can_invite_users: Optional[bool] = can_invite_users
self.can_pin_messages: Optional[bool] = can_pin_messages
self.can_manage_topics: Optional[bool] = can_manage_topics
self.can_send_audios: Optional[bool] = can_send_audios
self.can_send_documents: Optional[bool] = can_send_documents
self.can_send_photos: Optional[bool] = can_send_photos
self.can_send_videos: Optional[bool] = can_send_videos
self.can_send_video_notes: Optional[bool] = can_send_video_notes
self.can_send_voice_notes: Optional[bool] = can_send_voice_notes
self._id_attrs = (
self.can_send_messages,
@ -145,6 +216,19 @@ class ChatPermissions(TelegramObject):
self._freeze()
def __eq__(self, other: object) -> bool:
warn(
"In v21, granular media settings will be considered as well when comparing"
" ChatPermissions instances.",
PTBDeprecationWarning,
stacklevel=2,
)
return super().__eq__(other)
def __hash__(self) -> int:
# Intend: Added so support the own __eq__ function (which otherwise breaks hashing)
return super().__hash__()
@classmethod
def all_permissions(cls) -> "ChatPermissions":
"""
@ -155,7 +239,7 @@ class ChatPermissions(TelegramObject):
.. versionadded:: 20.0
"""
return cls(True, True, True, True, True, True, True, True, True)
return cls(*(15 * (True,)))
@classmethod
def no_permissions(cls) -> "ChatPermissions":
@ -165,4 +249,4 @@ class ChatPermissions(TelegramObject):
.. versionadded:: 20.0
"""
return cls(False, False, False, False, False, False, False, False, False)
return cls(*(15 * (False,)))

View file

@ -21,9 +21,12 @@
from typing import TYPE_CHECKING, Optional
from telegram._keyboardbuttonpolltype import KeyboardButtonPollType
from telegram._keyboardbuttonrequest import KeyboardButtonRequestChat, KeyboardButtonRequestUser
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
from telegram._utils.warnings import warn
from telegram._webappinfo import WebAppInfo
from telegram.warnings import PTBDeprecationWarning
if TYPE_CHECKING:
from telegram import Bot
@ -31,12 +34,12 @@ if TYPE_CHECKING:
class KeyboardButton(TelegramObject):
"""
This object represents one button of the reply keyboard. For simple text buttons String can be
used instead of this object to specify text of the button.
This object represents one button of the reply keyboard. For simple text buttons, :obj:`str`
can be used instead of this object to specify text of the button.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`text`, :attr:`request_contact`, :attr:`request_location`,
:attr:`request_poll` and :attr:`web_app` are equal.
:attr:`request_poll`, :attr:`web_app`, :attr:`request_user` and :attr:`request_chat` are equal.
Note:
* Optional fields are mutually exclusive.
@ -46,10 +49,16 @@ class KeyboardButton(TelegramObject):
January, 2020. Older clients will display unsupported message.
* :attr:`web_app` option will only work in Telegram versions released after 16 April, 2022.
Older clients will display unsupported message.
* :attr:`request_user` and :attr:`request_chat` options will only work in Telegram
versions released after 3 February, 2023. Older clients will display unsupported
message.
.. versionchanged:: 20.0
:attr:`web_app` is considered as well when comparing objects of this type in terms of
equality.
.. deprecated:: 20.1
:paramref:`request_user` and :paramref:`request_chat` will be considered as well when
comparing objects of this type in terms of equality in V21.
Args:
text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be
@ -67,6 +76,18 @@ class KeyboardButton(TelegramObject):
Available in private chats only.
.. versionadded:: 20.0
request_user (:class:`KeyboardButtonRequestUser`, optional): If specified, pressing the
button will open a list of suitable users. Tapping on any user will send its
identifier to the bot in a :attr:`telegram.Message.user_shared` service message.
Available in private chats only.
.. versionadded:: 20.1
request_chat (:class:`KeyboardButtonRequestChat`, optional): If specified, pressing the
button will open a list of suitable chats. Tapping on a chat will send its
identifier to the bot in a :attr:`telegram.Message.chat_shared` service message.
Available in private chats only.
.. versionadded:: 20.1
Attributes:
text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be
sent to the bot as a message when the button is pressed.
@ -83,9 +104,29 @@ class KeyboardButton(TelegramObject):
Available in private chats only.
.. versionadded:: 20.0
request_user (:class:`KeyboardButtonRequestUser`): Optional. If specified, pressing the
button will open a list of suitable users. Tapping on any user will send its
identifier to the bot in a :attr:`telegram.Message.user_shared` service message.
Available in private chats only.
.. versionadded:: 20.1
request_chat (:class:`KeyboardButtonRequestChat`): Optional. If specified, pressing the
button will open a list of suitable chats. Tapping on a chat will send its
identifier to the bot in a :attr:`telegram.Message.chat_shared` service message.
Available in private chats only.
.. versionadded:: 20.1
"""
__slots__ = ("request_location", "request_contact", "request_poll", "text", "web_app")
__slots__ = (
"request_location",
"request_contact",
"request_poll",
"text",
"web_app",
"request_user",
"request_chat",
)
def __init__(
self,
@ -94,6 +135,8 @@ class KeyboardButton(TelegramObject):
request_location: bool = None,
request_poll: KeyboardButtonPollType = None,
web_app: WebAppInfo = None,
request_user: KeyboardButtonRequestUser = None,
request_chat: KeyboardButtonRequestChat = None,
*,
api_kwargs: JSONDict = None,
):
@ -105,6 +148,8 @@ class KeyboardButton(TelegramObject):
self.request_location: Optional[bool] = request_location
self.request_poll: Optional[KeyboardButtonPollType] = request_poll
self.web_app: Optional[WebAppInfo] = web_app
self.request_user: Optional[KeyboardButtonRequestUser] = request_user
self.request_chat: Optional[KeyboardButtonRequestChat] = request_chat
self._id_attrs = (
self.text,
@ -116,6 +161,19 @@ class KeyboardButton(TelegramObject):
self._freeze()
def __eq__(self, other: object) -> bool:
warn(
"In v21, granular media settings will be considered as well when comparing"
" ChatPermissions instances.",
PTBDeprecationWarning,
stacklevel=2,
)
return super().__eq__(other)
def __hash__(self) -> int:
# Intend: Added so support the own __eq__ function (which otherwise breaks hashing)
return super().__hash__()
@classmethod
def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["KeyboardButton"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
@ -125,6 +183,8 @@ class KeyboardButton(TelegramObject):
return None
data["request_poll"] = KeyboardButtonPollType.de_json(data.get("request_poll"), bot)
data["request_user"] = KeyboardButtonRequestUser.de_json(data.get("request_user"), bot)
data["request_chat"] = KeyboardButtonRequestChat.de_json(data.get("request_chat"), bot)
data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot)
return super().de_json(data=data, bot=bot)

View file

@ -0,0 +1,197 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2023
# 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 two objects to request chats/users."""
from typing import TYPE_CHECKING, Optional
from telegram._chatadministratorrights import ChatAdministratorRights
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class KeyboardButtonRequestUser(TelegramObject):
"""This object defines the criteria used to request a suitable user. The identifier of the
selected user will be shared with the bot when the corresponding button is pressed.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`request_id` is equal.
.. versionadded:: 20.1
Args:
request_id (:obj:`int`): Signed 32-bit identifier of the request, which will be received
back in the :class:`telegram.UserShared` object. Must be unique within the message.
user_is_bot (:obj:`bool`, optional): Pass :obj:`True` to request a bot, pass :obj:`False`
to request a regular user. If not specified, no additional restrictions are applied.
user_is_premium (:obj:`bool`, optional): Pass :obj:`True` to request a premium user, pass
:obj:`False` to request a non-premium user. If not specified, no additional
restrictions are applied.
Attributes:
request_id (:obj:`int`): Identifier of the request.
user_is_bot (:obj:`bool`): Optional. Pass :obj:`True` to request a bot, pass :obj:`False`
to request a regular user. If not specified, no additional restrictions are applied.
user_is_premium (:obj:`bool`): Optional. Pass :obj:`True` to request a premium user, pass
:obj:`False` to request a non-premium user. If not specified, no additional
restrictions are applied.
"""
__slots__ = (
"request_id",
"user_is_bot",
"user_is_premium",
)
def __init__(
self,
request_id: int,
user_is_bot: bool = None,
user_is_premium: bool = None,
*,
api_kwargs: JSONDict = None, # skipcq: PYL-W0622
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.request_id: int = request_id
# Optionals
self.user_is_bot: Optional[bool] = user_is_bot
self.user_is_premium: Optional[bool] = user_is_premium
self._id_attrs = (self.request_id,)
self._freeze()
class KeyboardButtonRequestChat(TelegramObject):
"""This object defines the criteria used to request a suitable chat. The identifier of the
selected user will be shared with the bot when the corresponding button is pressed.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`request_id` is equal.
.. versionadded:: 20.1
Args:
request_id (:obj:`int`): Signed 32-bit identifier of the request, which will be received
back in the :class:`telegram.ChatShared` object. Must be unique within the message.
chat_is_channel (:obj:`bool`): Pass :obj:`True` to request a channel chat, pass
:obj:`False` to request a group or a supergroup chat.
chat_is_forum (:obj:`bool`, optional): Pass :obj:`True` to request a forum supergroup, pass
:obj:`False` to request a non-forum chat. If not specified, no additional
restrictions are applied.
chat_has_username (:obj:`bool`, optional): Pass :obj:`True` to request a supergroup or a
channel with a username, pass :obj:`False` to request a chat without a username. If
not specified, no additional restrictions are applied.
chat_is_created (:obj:`bool`, optional): Pass :obj:`True` to request a chat owned by the
user. Otherwise, no additional restrictions are applied.
user_administrator_rights (:class:`ChatAdministratorRights`, optional): Specifies the
required administrator rights of the user in the chat. If not specified, no additional
restrictions are applied.
bot_administrator_rights (:class:`ChatAdministratorRights`, optional): Specifies the
required administrator rights of the bot in the chat. The rights must be a subset of
:paramref:`user_administrator_rights`. If not specified, no additional restrictions are
applied.
bot_is_member (:obj:`bool`, optional): Pass :obj:`True` to request a chat with the bot
as a member. Otherwise, no additional restrictions are applied.
Attributes:
request_id (:obj:`int`): Identifier of the request.
chat_is_channel (:obj:`bool`): Pass :obj:`True` to request a channel chat, pass
:obj:`False` to request a group or a supergroup chat.
chat_is_forum (:obj:`bool`): Optional. Pass :obj:`True` to request a forum supergroup, pass
:obj:`False` to request a non-forum chat. If not specified, no additional
restrictions are applied.
chat_has_username (:obj:`bool`, optional): Pass :obj:`True` to request a supergroup or a
channel with a username, pass :obj:`False` to request a chat without a username. If
not specified, no additional restrictions are applied.
chat_is_created (:obj:`bool`) Optional. Pass :obj:`True` to request a chat owned by the
user. Otherwise, no additional restrictions are applied.
user_administrator_rights (:class:`ChatAdministratorRights`) Optional. Specifies the
required administrator rights of the user in the chat. If not specified, no additional
restrictions are applied.
bot_administrator_rights (:class:`ChatAdministratorRights`) Optional. Specifies the
required administrator rights of the bot in the chat. The rights must be a subset of
:attr:`user_administrator_rights`. If not specified, no additional restrictions are
applied.
bot_is_member (:obj:`bool`) Optional. Pass :obj:`True` to request a chat with the bot
as a member. Otherwise, no additional restrictions are applied.
"""
__slots__ = (
"request_id",
"chat_is_channel",
"chat_is_forum",
"chat_has_username",
"chat_is_created",
"user_administrator_rights",
"bot_administrator_rights",
"bot_is_member",
)
def __init__(
self,
request_id: int,
chat_is_channel: bool,
chat_is_forum: bool = None,
chat_has_username: bool = None,
chat_is_created: bool = None,
user_administrator_rights: ChatAdministratorRights = None,
bot_administrator_rights: ChatAdministratorRights = None,
bot_is_member: bool = None,
*,
api_kwargs: JSONDict = None, # skipcq: PYL-W0622
):
super().__init__(api_kwargs=api_kwargs)
# required
self.request_id: int = request_id
self.chat_is_channel: bool = chat_is_channel
# optional
self.chat_is_forum: Optional[bool] = chat_is_forum
self.chat_has_username: Optional[bool] = chat_has_username
self.chat_is_created: Optional[bool] = chat_is_created
self.user_administrator_rights: Optional[
ChatAdministratorRights
] = user_administrator_rights
self.bot_administrator_rights: Optional[ChatAdministratorRights] = bot_administrator_rights
self.bot_is_member: Optional[bool] = bot_is_member
self._id_attrs = (self.request_id,)
self._freeze()
@classmethod
def de_json(
cls, data: Optional[JSONDict], bot: "Bot"
) -> Optional["KeyboardButtonRequestChat"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if not data:
return None
data["user_administrator_rights"] = ChatAdministratorRights.de_json(
data.get("user_administrator_rights"), bot
)
data["bot_administrator_rights"] = ChatAdministratorRights.de_json(
data.get("bot_administrator_rights"), bot
)
return super().de_json(data=data, bot=bot)

View file

@ -53,6 +53,7 @@ from telegram._payment.invoice import Invoice
from telegram._payment.successfulpayment import SuccessfulPayment
from telegram._poll import Poll
from telegram._proximityalerttriggered import ProximityAlertTriggered
from telegram._shared import ChatShared, UserShared
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.argumentparsing import parse_sequence_arg
@ -316,6 +317,14 @@ class Message(TelegramObject):
by a spoiler animation.
.. versionadded:: 20.0
user_shared (:class:`telegram.UserShared`, optional): Service message: a user was shared
with the bot.
.. versionadded:: 20.1
chat_shared (:class:`telegram.ChatShared`, optional):Service message: a chat was shared
with the bot.
.. versionadded:: 20.1
Attributes:
message_id (:obj:`int`): Unique message identifier inside this chat.
@ -543,6 +552,14 @@ class Message(TelegramObject):
by a spoiler animation.
.. versionadded:: 20.0
user_shared (:class:`telegram.UserShared`): Optional. Service message: a user was shared
with the bot.
.. versionadded:: 20.1
chat_shared (:class:`telegram.ChatShared`): Optional. Service message: a chat was shared
with the bot.
.. versionadded:: 20.1
.. |custom_emoji_formatting_note| replace:: Custom emoji entities will currently be ignored
by this function. Instead, the supplied replacement for the emoji will be used.
@ -620,6 +637,8 @@ class Message(TelegramObject):
"general_forum_topic_unhidden",
"write_access_allowed",
"has_media_spoiler",
"user_shared",
"chat_shared",
)
def __init__(
@ -693,6 +712,8 @@ class Message(TelegramObject):
general_forum_topic_unhidden: GeneralForumTopicUnhidden = None,
write_access_allowed: WriteAccessAllowed = None,
has_media_spoiler: bool = None,
user_shared: UserShared = None,
chat_shared: ChatShared = None,
*,
api_kwargs: JSONDict = None,
):
@ -779,6 +800,8 @@ class Message(TelegramObject):
] = general_forum_topic_unhidden
self.write_access_allowed: Optional[WriteAccessAllowed] = write_access_allowed
self.has_media_spoiler: Optional[bool] = has_media_spoiler
self.user_shared: Optional[UserShared] = user_shared
self.chat_shared: Optional[ChatShared] = chat_shared
self._effective_attachment = DEFAULT_NONE
@ -888,6 +911,8 @@ class Message(TelegramObject):
data["write_access_allowed"] = WriteAccessAllowed.de_json(
data.get("write_access_allowed"), bot
)
data["user_shared"] = UserShared.de_json(data.get("user_shared"), bot)
data["chat_shared"] = ChatShared.de_json(data.get("chat_shared"), bot)
return super().de_json(data=data, bot=bot)

107
telegram/_shared.py Normal file
View file

@ -0,0 +1,107 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2023
# 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 two objects used for request chats/users service messages."""
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
class UserShared(TelegramObject):
"""
This object contains information about the user whose identifier was shared with the bot
using a :class:`telegram.KeyboardButtonRequestUser` button.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`request_id` and :attr:`user_id` are equal.
.. versionadded:: 20.1
Args:
request_id (:obj:`int`): Identifier of the request.
user_id (:obj:`int`): Identifier of the shared user. This number may be greater than 32
bits and some programming languages may have difficulty/silent defects in interpreting
it. But it is smaller than 52 bits, so a signed 64-bit integer or double-precision
float type are safe for storing this identifier.
Attributes:
request_id (:obj:`int`): Identifier of the request.
user_id (:obj:`int`): Identifier of the shared user. This number may be greater than 32
bits and some programming languages may have difficulty/silent defects in interpreting
it. But it is smaller than 52 bits, so a signed 64-bit integer or double-precision
float type are safe for storing this identifier.
"""
__slots__ = ("request_id", "user_id")
def __init__(
self,
request_id: int,
user_id: int,
*,
api_kwargs: JSONDict = None,
):
super().__init__(api_kwargs=api_kwargs)
self.request_id: int = request_id
self.user_id: int = user_id
self._id_attrs = (self.request_id, self.user_id)
self._freeze()
class ChatShared(TelegramObject):
"""
This object contains information about the chat whose identifier was shared with the bot
using a :class:`telegram.KeyboardButtonRequestChat` button.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`request_id` and :attr:`chat_id` are equal.
.. versionadded:: 20.1
Args:
request_id (:obj:`int`): Identifier of the request.
chat_id (:obj:`int`): Identifier of the shared user. This number may be greater than 32
bits and some programming languages may have difficulty/silent defects in interpreting
it. But it is smaller than 52 bits, so a signed 64-bit integer or double-precision
float type are safe for storing this identifier.
Attributes:
request_id (:obj:`int`): Identifier of the request.
chat_id (:obj:`int`): Identifier of the shared user. This number may be greater than 32
bits and some programming languages may have difficulty/silent defects in interpreting
it. But it is smaller than 52 bits, so a signed 64-bit integer or double-precision
float type are safe for storing this identifier.
"""
__slots__ = ("request_id", "chat_id")
def __init__(
self,
request_id: int,
chat_id: int,
*,
api_kwargs: JSONDict = None,
):
super().__init__(api_kwargs=api_kwargs)
self.request_id: int = request_id
self.chat_id: int = chat_id
self._id_attrs = (self.request_id, self.chat_id)
self._freeze()

View file

@ -109,6 +109,9 @@ class User(TelegramObject):
the bot to the attachment menu.
.. versionadded:: 20.0
.. |user_chat_id_note| replace:: This shortcuts build on the assumption that :attr:`User.id`
coincides with the :attr:`Chat.id` of the private chat with the user. This has been the
case so far, but Telegram does not guarantee that this stays this way.
"""
__slots__ = (
@ -293,6 +296,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`.
Note:
|user_chat_id_note|
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@ -324,6 +330,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`.
Note:
|user_chat_id_note|
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@ -354,6 +363,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see
:meth:`telegram.Bot.unpin_all_chat_messages`.
Note:
|user_chat_id_note|
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@ -392,6 +404,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -442,6 +457,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_photo`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -493,6 +511,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`.
Note:
|user_chat_id_note|
Returns:
Tuple[:class:`telegram.Message`:] On success, a tuple of :class:`~telegram.Message`
instances that were sent is returned.
@ -546,6 +567,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_audio`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -591,6 +615,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_chat_action`.
Note:
|user_chat_id_note|
Returns:
:obj:`True`: On success.
@ -635,6 +662,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_contact`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -681,6 +711,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_dice`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -729,6 +762,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_document`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -777,6 +813,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_game`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -845,6 +884,9 @@ class User(TelegramObject):
order of the arguments had to be changed. Use keyword arguments to make sure that the
arguments are passed correctly.
Note:
|user_chat_id_note|
.. versionchanged:: 13.5
As of Bot API 5.2, the parameter
:paramref:`start_parameter <telegram.Bot.send_invoice.start_parameter>` is optional.
@ -917,6 +959,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_location`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -974,6 +1019,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_animation`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1025,6 +1073,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_sticker`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1077,6 +1128,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_video`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1137,6 +1191,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_venue`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1191,6 +1248,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_video_note`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1242,6 +1302,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_voice`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1300,6 +1363,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_poll`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1357,6 +1423,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1407,6 +1476,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`.
Note:
|user_chat_id_note|
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
@ -1448,6 +1520,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see
:meth:`telegram.Bot.approve_chat_join_request`.
Note:
|user_chat_id_note|
.. versionadded:: 13.8
Returns:
@ -1481,6 +1556,9 @@ class User(TelegramObject):
For the documentation of the arguments, please see
:meth:`telegram.Bot.decline_chat_join_request`.
Note:
|user_chat_id_note|
.. versionadded:: 13.8
Returns:
@ -1516,6 +1594,9 @@ class User(TelegramObject):
.. seealso:: :meth:`get_menu_button`
Note:
|user_chat_id_note|
.. versionadded:: 20.0
Returns:
@ -1549,6 +1630,9 @@ class User(TelegramObject):
.. seealso:: :meth:`set_menu_button`
Note:
|user_chat_id_note|
.. versionadded:: 20.0
Returns:

View file

@ -111,7 +111,7 @@ class _BotAPIVersion(NamedTuple):
#: :data:`telegram.__bot_api_version_info__`.
#:
#: .. versionadded:: 20.0
BOT_API_VERSION_INFO = _BotAPIVersion(major=6, minor=4)
BOT_API_VERSION_INFO = _BotAPIVersion(major=6, minor=5)
#: :obj:`str`: Telegram Bot API
#: version supported by this version of `python-telegram-bot`. Also available as
#: :data:`telegram.__bot_api_version__`.

View file

@ -2088,6 +2088,7 @@ class ExtBot(Bot, Generic[RLARGS]):
user_id: Union[str, int],
permissions: ChatPermissions,
until_date: Union[int, datetime] = None,
use_independent_chat_permissions: bool = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@ -2101,6 +2102,7 @@ class ExtBot(Bot, Generic[RLARGS]):
user_id=user_id,
permissions=permissions,
until_date=until_date,
use_independent_chat_permissions=use_independent_chat_permissions,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
@ -3008,6 +3010,7 @@ class ExtBot(Bot, Generic[RLARGS]):
self,
chat_id: Union[str, int],
permissions: ChatPermissions,
use_independent_chat_permissions: bool = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@ -3019,6 +3022,7 @@ class ExtBot(Bot, Generic[RLARGS]):
return await super().set_chat_permissions(
chat_id=chat_id,
permissions=permissions,
use_independent_chat_permissions=use_independent_chat_permissions,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,

View file

@ -1760,6 +1760,8 @@ class StatusUpdate:
or StatusUpdate.GENERAL_FORUM_TOPIC_HIDDEN.check_update(update)
or StatusUpdate.GENERAL_FORUM_TOPIC_UNHIDDEN.check_update(update)
or StatusUpdate.WRITE_ACCESS_ALLOWED.check_update(update)
or StatusUpdate.USER_SHARED.check_update(update)
or StatusUpdate.CHAT_SHARED.check_update(update)
)
ALL = _All(name="filters.StatusUpdate.ALL")
@ -1780,6 +1782,18 @@ class StatusUpdate:
:attr:`telegram.Message.supergroup_chat_created` or
:attr:`telegram.Message.channel_chat_created`."""
class _ChatShared(MessageFilter):
__slots__ = ()
def filter(self, message: Message) -> bool:
return bool(message.chat_shared)
CHAT_SHARED = _ChatShared(name="filters.StatusUpdate.CHAT_SHARED")
"""Messages that contain :attr:`telegram.Message.chat_shared`.
.. versionadded:: 20.1
"""
class _ConnectedWebsite(MessageFilter):
__slots__ = ()
@ -1954,6 +1968,18 @@ class StatusUpdate:
)
"""Messages that contain :attr:`telegram.Message.proximity_alert_triggered`."""
class _UserShared(MessageFilter):
__slots__ = ()
def filter(self, message: Message) -> bool:
return bool(message.user_shared)
USER_SHARED = _UserShared(name="filters.StatusUpdate.USER_SHARED")
"""Messages that contain :attr:`telegram.Message.user_shared`.
.. versionadded:: 20.1
"""
class _VideoChatEnded(MessageFilter):
__slots__ = ()

View file

@ -1584,11 +1584,12 @@ class TestBot:
data = request_data.json_parameters
chat_id = data["chat_id"] == "2"
permissions = data["permissions"] == chat_permissions.to_json()
return chat_id and permissions
use_independent_chat_permissions = data["use_independent_chat_permissions"]
return chat_id and permissions and use_independent_chat_permissions
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.set_chat_permissions(2, chat_permissions)
assert await bot.set_chat_permissions(2, chat_permissions, True)
async def test_set_chat_administrator_custom_title(self, monkeypatch, bot):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
@ -2183,14 +2184,26 @@ class TestBot:
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.answer_pre_checkout_query(1, False, error_message="Not enough fish")
@pytest.mark.flaky(3, 1)
async def test_restrict_chat_member(self, bot, channel_id, chat_permissions):
# TODO: Add bot to supergroup so this can be tested properly
with pytest.raises(BadRequest, match="Method is available only for supergroups"):
assert await bot.restrict_chat_member(
channel_id, 95205500, chat_permissions, until_date=dtm.datetime.utcnow()
async def test_restrict_chat_member(self, bot, chat_permissions, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
chat_id = data["chat_id"] == "@chat"
user_id = data["user_id"] == "2"
permissions = data["permissions"] == chat_permissions.to_json()
until_date = data["until_date"] == "200"
use_independent_chat_permissions = data["use_independent_chat_permissions"]
return (
chat_id
and user_id
and permissions
and until_date
and use_independent_chat_permissions
)
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.restrict_chat_member("@chat", 2, chat_permissions, 200, True)
async def test_restrict_chat_member_default_tz(
self, monkeypatch, tz_bot, channel_id, chat_permissions
):

View file

@ -42,6 +42,7 @@ def chat_join_request(bot, time):
date=time,
bio=TestChatJoinRequest.bio,
invite_link=TestChatJoinRequest.invite_link,
user_chat_id=TestChatJoinRequest.from_user.id,
)
cjr.set_bot(bot)
return cjr
@ -71,6 +72,7 @@ class TestChatJoinRequest:
"chat": self.chat.to_dict(),
"from": self.from_user.to_dict(),
"date": to_timestamp(time),
"user_chat_id": self.from_user.id,
}
chat_join_request = ChatJoinRequest.de_json(json_dict, bot)
assert chat_join_request.api_kwargs == {}
@ -79,6 +81,7 @@ class TestChatJoinRequest:
assert chat_join_request.from_user == self.from_user
assert abs(chat_join_request.date - time) < datetime.timedelta(seconds=1)
assert to_timestamp(chat_join_request.date) == to_timestamp(time)
assert chat_join_request.user_chat_id == self.from_user.id
json_dict.update({"bio": self.bio, "invite_link": self.invite_link.to_dict()})
chat_join_request = ChatJoinRequest.de_json(json_dict, bot)
@ -88,6 +91,7 @@ class TestChatJoinRequest:
assert chat_join_request.from_user == self.from_user
assert abs(chat_join_request.date - time) < datetime.timedelta(seconds=1)
assert to_timestamp(chat_join_request.date) == to_timestamp(time)
assert chat_join_request.user_chat_id == self.from_user.id
assert chat_join_request.bio == self.bio
assert chat_join_request.invite_link == self.invite_link
@ -100,13 +104,16 @@ class TestChatJoinRequest:
assert chat_join_request_dict["date"] == to_timestamp(chat_join_request.date)
assert chat_join_request_dict["bio"] == chat_join_request.bio
assert chat_join_request_dict["invite_link"] == chat_join_request.invite_link.to_dict()
assert chat_join_request_dict["user_chat_id"] == self.from_user.id
def test_equality(self, chat_join_request, time):
a = chat_join_request
b = ChatJoinRequest(self.chat, self.from_user, time)
c = ChatJoinRequest(self.chat, self.from_user, time, bio="bio")
d = ChatJoinRequest(self.chat, self.from_user, time + datetime.timedelta(1))
e = ChatJoinRequest(self.chat, User(-1, "last_name", True), time)
b = ChatJoinRequest(self.chat, self.from_user, time, self.from_user.id)
c = ChatJoinRequest(self.chat, self.from_user, time, self.from_user.id, bio="bio")
d = ChatJoinRequest(
self.chat, self.from_user, time + datetime.timedelta(1), self.from_user.id
)
e = ChatJoinRequest(self.chat, User(-1, "last_name", True), time, -1)
f = User(456, "", False)
assert a == b

View file

@ -89,6 +89,7 @@ def chat_join_request(time, bot):
is_revoked=False,
is_primary=False,
),
user_chat_id=2,
)
cjr.set_bot(bot)
return cjr

View file

@ -61,6 +61,12 @@ class CMDefaults:
can_manage_chat: bool = True
can_manage_video_chats: bool = True
can_manage_topics: bool = True
can_send_audios: bool = True
can_send_documents: bool = True
can_send_photos: bool = True
can_send_videos: bool = True
can_send_video_notes: bool = True
can_send_voice_notes: bool = True
def chat_member_owner():
@ -105,6 +111,12 @@ def chat_member_restricted():
CMDefaults.can_add_web_page_previews,
CMDefaults.can_manage_topics,
CMDefaults.until_date,
CMDefaults.can_send_audios,
CMDefaults.can_send_documents,
CMDefaults.can_send_photos,
CMDefaults.can_send_videos,
CMDefaults.can_send_video_notes,
CMDefaults.can_send_voice_notes,
)

View file

@ -34,6 +34,12 @@ def chat_permissions():
can_invite_users=True,
can_pin_messages=True,
can_manage_topics=True,
can_send_audios=True,
can_send_documents=True,
can_send_photos=True,
can_send_videos=True,
can_send_video_notes=True,
can_send_voice_notes=True,
)
@ -47,6 +53,12 @@ class TestChatPermissions:
can_invite_users = None
can_pin_messages = None
can_manage_topics = None
can_send_audios = True
can_send_documents = False
can_send_photos = None
can_send_videos = True
can_send_video_notes = False
can_send_voice_notes = None
def test_slot_behaviour(self, chat_permissions, mro_slots):
inst = chat_permissions
@ -64,7 +76,12 @@ class TestChatPermissions:
"can_change_info": self.can_change_info,
"can_invite_users": self.can_invite_users,
"can_pin_messages": self.can_pin_messages,
"can_manage_topics": self.can_manage_topics,
"can_send_audios": self.can_send_audios,
"can_send_documents": self.can_send_documents,
"can_send_photos": self.can_send_photos,
"can_send_videos": self.can_send_videos,
"can_send_video_notes": self.can_send_video_notes,
"can_send_voice_notes": self.can_send_voice_notes,
}
permissions = ChatPermissions.de_json(json_dict, bot)
assert permissions.api_kwargs == {}
@ -78,6 +95,12 @@ class TestChatPermissions:
assert permissions.can_invite_users == self.can_invite_users
assert permissions.can_pin_messages == self.can_pin_messages
assert permissions.can_manage_topics == self.can_manage_topics
assert permissions.can_send_audios == self.can_send_audios
assert permissions.can_send_documents == self.can_send_documents
assert permissions.can_send_photos == self.can_send_photos
assert permissions.can_send_videos == self.can_send_videos
assert permissions.can_send_video_notes == self.can_send_video_notes
assert permissions.can_send_voice_notes == self.can_send_voice_notes
def test_to_dict(self, chat_permissions):
permissions_dict = chat_permissions.to_dict()
@ -99,6 +122,12 @@ class TestChatPermissions:
assert permissions_dict["can_invite_users"] == chat_permissions.can_invite_users
assert permissions_dict["can_pin_messages"] == chat_permissions.can_pin_messages
assert permissions_dict["can_manage_topics"] == chat_permissions.can_manage_topics
assert permissions_dict["can_send_audios"] == chat_permissions.can_send_audios
assert permissions_dict["can_send_documents"] == chat_permissions.can_send_documents
assert permissions_dict["can_send_photos"] == chat_permissions.can_send_photos
assert permissions_dict["can_send_videos"] == chat_permissions.can_send_videos
assert permissions_dict["can_send_video_notes"] == chat_permissions.can_send_video_notes
assert permissions_dict["can_send_voice_notes"] == chat_permissions.can_send_voice_notes
def test_equality(self):
a = ChatPermissions(
@ -120,6 +149,18 @@ class TestChatPermissions:
can_send_other_messages=False,
)
d = User(123, "", False)
e = ChatPermissions(
can_send_messages=True,
can_send_media_messages=True,
can_send_polls=True,
can_send_other_messages=False,
can_send_audios=True,
can_send_documents=True,
can_send_photos=True,
can_send_videos=True,
can_send_video_notes=True,
can_send_voice_notes=True,
)
assert a == b
assert hash(a) == hash(b)
@ -131,6 +172,10 @@ class TestChatPermissions:
assert a != d
assert hash(a) != hash(d)
# we expect this to be true since we don't compare these in V20
assert a == e
assert hash(a) == hash(e)
def test_all_permissions(self):
f = ChatPermissions()
t = ChatPermissions.all_permissions()
@ -149,8 +194,18 @@ class TestChatPermissions:
# if the dirs are the same, the attributes will all be there
assert dir(f) == dir(t)
# now we just need to check that all attributes are True. _id_attrs returns all values,
# if a new one is added without defaulting to True, this will fail
# if a new one is added without defaulting to False, this will fail
for key in t.__slots__:
assert t[key] is False
# and as a finisher, make sure the default is different.
assert f != t
def test_equality_warning(self, recwarn, chat_permissions):
recwarn.clear()
assert chat_permissions == chat_permissions
assert str(recwarn[0].message) == (
"In v21, granular media settings will be considered as well when comparing"
" ChatPermissions instances."
)

View file

@ -1046,6 +1046,16 @@ class TestFilters:
assert filters.StatusUpdate.WRITE_ACCESS_ALLOWED.check_update(update)
update.message.write_access_allowed = None
update.message.user_shared = "user_shared"
assert filters.StatusUpdate.ALL.check_update(update)
assert filters.StatusUpdate.USER_SHARED.check_update(update)
update.message.user_shared = None
update.message.chat_shared = "user_shared"
assert filters.StatusUpdate.ALL.check_update(update)
assert filters.StatusUpdate.CHAT_SHARED.check_update(update)
update.message.chat_shared = None
def test_filters_forwarded(self, update):
assert not filters.FORWARDED.check_update(update)
update.message.forward_date = datetime.datetime.utcnow()

View file

@ -18,7 +18,14 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
import pytest
from telegram import InlineKeyboardButton, KeyboardButton, KeyboardButtonPollType, WebAppInfo
from telegram import (
InlineKeyboardButton,
KeyboardButton,
KeyboardButtonPollType,
KeyboardButtonRequestChat,
KeyboardButtonRequestUser,
WebAppInfo,
)
@pytest.fixture(scope="class")
@ -29,6 +36,8 @@ def keyboard_button():
request_contact=TestKeyboardButton.request_contact,
request_poll=TestKeyboardButton.request_poll,
web_app=TestKeyboardButton.web_app,
request_chat=TestKeyboardButton.request_chat,
request_user=TestKeyboardButton.request_user,
)
@ -38,6 +47,8 @@ class TestKeyboardButton:
request_contact = True
request_poll = KeyboardButtonPollType("quiz")
web_app = WebAppInfo(url="https://example.com")
request_chat = KeyboardButtonRequestChat(1, True)
request_user = KeyboardButtonRequestUser(2)
def test_slot_behaviour(self, keyboard_button, mro_slots):
inst = keyboard_button
@ -51,6 +62,8 @@ class TestKeyboardButton:
assert keyboard_button.request_contact == self.request_contact
assert keyboard_button.request_poll == self.request_poll
assert keyboard_button.web_app == self.web_app
assert keyboard_button.request_chat == self.request_chat
assert keyboard_button.request_user == self.request_user
def test_to_dict(self, keyboard_button):
keyboard_button_dict = keyboard_button.to_dict()
@ -61,6 +74,8 @@ class TestKeyboardButton:
assert keyboard_button_dict["request_contact"] == keyboard_button.request_contact
assert keyboard_button_dict["request_poll"] == keyboard_button.request_poll.to_dict()
assert keyboard_button_dict["web_app"] == keyboard_button.web_app.to_dict()
assert keyboard_button_dict["request_chat"] == keyboard_button.request_chat.to_dict()
assert keyboard_button_dict["request_user"] == keyboard_button.request_user.to_dict()
def test_de_json(self, bot):
json_dict = {
@ -69,6 +84,8 @@ class TestKeyboardButton:
"request_contact": self.request_contact,
"request_poll": self.request_poll.to_dict(),
"web_app": self.web_app.to_dict(),
"request_chat": self.request_chat.to_dict(),
"request_user": self.request_user.to_dict(),
}
inline_keyboard_button = KeyboardButton.de_json(json_dict, None)
@ -78,6 +95,8 @@ class TestKeyboardButton:
assert inline_keyboard_button.request_contact == self.request_contact
assert inline_keyboard_button.request_poll == self.request_poll
assert inline_keyboard_button.web_app == self.web_app
assert inline_keyboard_button.request_chat == self.request_chat
assert inline_keyboard_button.request_user == self.request_user
none = KeyboardButton.de_json({}, None)
assert none is None
@ -88,6 +107,12 @@ class TestKeyboardButton:
c = KeyboardButton("Test", request_location=True)
d = KeyboardButton("Test", web_app=WebAppInfo(url="https://ptb.org"))
e = InlineKeyboardButton("test", callback_data="test")
f = KeyboardButton(
"test",
request_contact=True,
request_chat=KeyboardButtonRequestChat(1, False),
request_user=KeyboardButtonRequestUser(2),
)
assert a == b
assert hash(a) == hash(b)
@ -100,3 +125,17 @@ class TestKeyboardButton:
assert a != e
assert hash(a) != hash(e)
# we expect this to be true since we don't compare these in V20
assert a == f
assert hash(a) == hash(f)
def test_equality_warning(self, recwarn, keyboard_button):
recwarn.clear()
assert keyboard_button == keyboard_button
assert str(recwarn[0].message) == (
"In v21, granular media settings will be considered as well when comparing"
" ChatPermissions instances."
)

View file

@ -0,0 +1,163 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2023
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import pytest
from telegram import ChatAdministratorRights, KeyboardButtonRequestChat, KeyboardButtonRequestUser
@pytest.fixture(scope="class")
def request_user():
return KeyboardButtonRequestUser(
TestKeyboardButtonRequestUser.request_id,
TestKeyboardButtonRequestUser.user_is_bot,
TestKeyboardButtonRequestUser.user_is_premium,
)
class TestKeyboardButtonRequestUser:
request_id = 123
user_is_bot = True
user_is_premium = False
def test_slot_behaviour(self, request_user, mro_slots):
for attr in request_user.__slots__:
assert getattr(request_user, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(request_user)) == len(set(mro_slots(request_user))), "duplicate slot"
def test_to_dict(self, request_user):
request_user_dict = request_user.to_dict()
assert isinstance(request_user_dict, dict)
assert request_user_dict["request_id"] == self.request_id
assert request_user_dict["user_is_bot"] == self.user_is_bot
assert request_user_dict["user_is_premium"] == self.user_is_premium
def test_de_json(self, bot):
json_dict = {
"request_id": self.request_id,
"user_is_bot": self.user_is_bot,
"user_is_premium": self.user_is_premium,
}
request_user = KeyboardButtonRequestUser.de_json(json_dict, bot)
assert request_user.api_kwargs == {}
assert request_user.request_id == self.request_id
assert request_user.user_is_bot == self.user_is_bot
assert request_user.user_is_premium == self.user_is_premium
def test_equality(self):
a = KeyboardButtonRequestUser(self.request_id)
b = KeyboardButtonRequestUser(self.request_id)
c = KeyboardButtonRequestUser(1)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)
@pytest.fixture(scope="class")
def request_chat():
return KeyboardButtonRequestChat(
TestKeyboardButtonRequestChat.request_id,
TestKeyboardButtonRequestChat.chat_is_channel,
TestKeyboardButtonRequestChat.chat_is_forum,
TestKeyboardButtonRequestChat.chat_has_username,
TestKeyboardButtonRequestChat.chat_is_created,
TestKeyboardButtonRequestChat.user_administrator_rights,
TestKeyboardButtonRequestChat.bot_administrator_rights,
TestKeyboardButtonRequestChat.bot_is_member,
)
class TestKeyboardButtonRequestChat:
request_id = 456
chat_is_channel = True
chat_is_forum = False
chat_has_username = True
chat_is_created = False
user_administrator_rights = ChatAdministratorRights(
True, False, True, False, True, False, True, False
)
bot_administrator_rights = ChatAdministratorRights(
True, False, True, False, True, False, True, False
)
bot_is_member = True
def test_slot_behaviour(self, request_chat, mro_slots):
for attr in request_chat.__slots__:
assert getattr(request_chat, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(request_chat)) == len(set(mro_slots(request_chat))), "duplicate slot"
def test_to_dict(self, request_chat):
request_chat_dict = request_chat.to_dict()
assert isinstance(request_chat_dict, dict)
assert request_chat_dict["request_id"] == self.request_id
assert request_chat_dict["chat_is_channel"] == self.chat_is_channel
assert request_chat_dict["chat_is_forum"] == self.chat_is_forum
assert request_chat_dict["chat_has_username"] == self.chat_has_username
assert (
request_chat_dict["user_administrator_rights"]
== self.user_administrator_rights.to_dict()
)
assert (
request_chat_dict["bot_administrator_rights"]
== self.bot_administrator_rights.to_dict()
)
assert request_chat_dict["bot_is_member"] == self.bot_is_member
def test_de_json(self, bot):
json_dict = {
"request_id": self.request_id,
"chat_is_channel": self.chat_is_channel,
"chat_is_forum": self.chat_is_forum,
"chat_has_username": self.chat_has_username,
"user_administrator_rights": self.user_administrator_rights.to_dict(),
"bot_administrator_rights": self.bot_administrator_rights.to_dict(),
"bot_is_member": self.bot_is_member,
}
request_chat = KeyboardButtonRequestChat.de_json(json_dict, bot)
assert request_chat.api_kwargs == {}
assert request_chat.request_id == self.request_id
assert request_chat.chat_is_channel == self.chat_is_channel
assert request_chat.chat_is_forum == self.chat_is_forum
assert request_chat.chat_has_username == self.chat_has_username
assert request_chat.user_administrator_rights == self.user_administrator_rights
assert request_chat.bot_administrator_rights == self.bot_administrator_rights
assert request_chat.bot_is_member == self.bot_is_member
empty_chat = KeyboardButtonRequestChat.de_json({}, bot)
assert empty_chat is None
def test_equality(self):
a = KeyboardButtonRequestChat(self.request_id, True)
b = KeyboardButtonRequestChat(self.request_id, True)
c = KeyboardButtonRequestChat(1, True)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)

View file

@ -26,6 +26,7 @@ from telegram import (
Audio,
Bot,
Chat,
ChatShared,
Contact,
Dice,
Document,
@ -44,6 +45,7 @@ from telegram import (
SuccessfulPayment,
Update,
User,
UserShared,
Venue,
Video,
VideoChatEnded,
@ -207,6 +209,8 @@ def message(bot):
},
{"web_app_data": WebAppData("some_data", "some_button_text")},
{"message_thread_id": 123},
{"user_shared": UserShared(1, 2)},
{"chat_shared": ChatShared(3, 4)},
],
ids=[
"forwarded_user",
@ -261,6 +265,8 @@ def message(bot):
"entities",
"web_app_data",
"message_thread_id",
"user_shared",
"chat_shared",
],
)
def message_params(bot, request):

View file

@ -194,6 +194,8 @@ def check_object(h4):
ignored |= {"caption", "caption_entities", "media", "media_type", "parse_mode"}
elif name.startswith("InputMedia"):
ignored |= {"filename"} # Convenience parameter
elif name in ("ChatMemberRestricted", "ChatPermissions"):
ignored |= {"can_send_media_messages"} # Depreciated param we keep
assert (sig.parameters.keys() ^ checked) - ignored == set()

126
tests/test_shared.py Normal file
View file

@ -0,0 +1,126 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2023
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import pytest
from telegram import ChatShared, UserShared
@pytest.fixture(scope="class")
def user_shared():
return UserShared(
TestUserShared.request_id,
TestUserShared.user_id,
)
class TestUserShared:
request_id = 789
user_id = 101112
def test_slot_behaviour(self, user_shared, mro_slots):
for attr in user_shared.__slots__:
assert getattr(user_shared, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(user_shared)) == len(set(mro_slots(user_shared))), "duplicate slot"
def test_to_dict(self, user_shared):
user_shared_dict = user_shared.to_dict()
assert isinstance(user_shared_dict, dict)
assert user_shared_dict["request_id"] == self.request_id
assert user_shared_dict["user_id"] == self.user_id
def test_de_json(self, bot):
json_dict = {
"request_id": self.request_id,
"user_id": self.user_id,
}
user_shared = UserShared.de_json(json_dict, bot)
assert user_shared.api_kwargs == {}
assert user_shared.request_id == self.request_id
assert user_shared.user_id == self.user_id
def test_equality(self):
a = UserShared(self.request_id, self.user_id)
b = UserShared(self.request_id, self.user_id)
c = UserShared(1, self.user_id)
d = UserShared(self.request_id, 1)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
@pytest.fixture(scope="class")
def chat_shared():
return ChatShared(
TestChatShared.request_id,
TestChatShared.chat_id,
)
class TestChatShared:
request_id = 131415
chat_id = 161718
def test_slot_behaviour(self, chat_shared, mro_slots):
for attr in chat_shared.__slots__:
assert getattr(chat_shared, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(chat_shared)) == len(set(mro_slots(chat_shared))), "duplicate slot"
def test_to_dict(self, chat_shared):
chat_shared_dict = chat_shared.to_dict()
assert isinstance(chat_shared_dict, dict)
assert chat_shared_dict["request_id"] == self.request_id
assert chat_shared_dict["chat_id"] == self.chat_id
def test_de_json(self, bot):
json_dict = {
"request_id": self.request_id,
"chat_id": self.chat_id,
}
chat_shared = ChatShared.de_json(json_dict, bot)
assert chat_shared.api_kwargs == {}
assert chat_shared.request_id == self.request_id
assert chat_shared.chat_id == self.chat_id
def test_equality(self):
a = ChatShared(self.request_id, self.chat_id)
b = ChatShared(self.request_id, self.chat_id)
c = ChatShared(1, self.chat_id)
d = ChatShared(self.request_id, 1)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)

View file

@ -54,6 +54,7 @@ chat_join_request = ChatJoinRequest(
chat=Chat(1, Chat.SUPERGROUP),
from_user=User(1, "first_name", False),
date=from_timestamp(int(time.time())),
user_chat_id=1,
bio="bio",
)