From a6263cdc2ed09f7b7b03ad8615787243ed0de6b4 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:24:40 +0200 Subject: [PATCH 1/5] Add BusinessBotRights --- docs/source/telegram.at-tree.rst | 1 + docs/source/telegram.businessbotrights.rst | 6 + telegram/__init__.py | 2 + telegram/_business.py | 186 ++++++++++++++++++++- telegram/_utils/warnings_transition.py | 4 +- tests/test_business_classes.py | 178 +++++++++++++++++++- 6 files changed, 360 insertions(+), 17 deletions(-) create mode 100644 docs/source/telegram.businessbotrights.rst diff --git a/docs/source/telegram.at-tree.rst b/docs/source/telegram.at-tree.rst index 22abbfb3867..5ea6949e815 100644 --- a/docs/source/telegram.at-tree.rst +++ b/docs/source/telegram.at-tree.rst @@ -19,6 +19,7 @@ Available Types telegram.botdescription telegram.botname telegram.botshortdescription + telegram.businessbotrights telegram.businessconnection telegram.businessintro telegram.businesslocation diff --git a/docs/source/telegram.businessbotrights.rst b/docs/source/telegram.businessbotrights.rst new file mode 100644 index 00000000000..d6bdab1a809 --- /dev/null +++ b/docs/source/telegram.businessbotrights.rst @@ -0,0 +1,6 @@ +BusinessBotRights +================= + +.. autoclass:: telegram.BusinessBotRights + :members: + :show-inheritance: \ No newline at end of file diff --git a/telegram/__init__.py b/telegram/__init__.py index fe2fce247ea..5b444129261 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -46,6 +46,7 @@ "BotDescription", "BotName", "BotShortDescription", + "BusinessBotRights", "BusinessConnection", "BusinessIntro", "BusinessLocation", @@ -301,6 +302,7 @@ from ._botdescription import BotDescription, BotShortDescription from ._botname import BotName from ._business import ( + BusinessBotRights, BusinessConnection, BusinessIntro, BusinessLocation, diff --git a/telegram/_business.py b/telegram/_business.py index 95607c24344..7d006016631 100644 --- a/telegram/_business.py +++ b/telegram/_business.py @@ -30,20 +30,167 @@ from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp from telegram._utils.types import JSONDict +from telegram._utils.warnings_transition import warn_about_deprecated_attr_in_property if TYPE_CHECKING: from telegram import Bot +class BusinessBotRights(TelegramObject): + """ + This object represents the rights of a business bot. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if all their attributes are equal. + + .. versionadded:: NEXT.VERSION + + Args: + can_reply (:obj:`bool`, optional): True, if the bot can send and edit messages in the + private chats that had incoming messages in the last 24 hours. + can_read_messages (:obj:`bool`, optional): True, if the bot can mark incoming private + messages as read. + can_delete_outgoing_messages (:obj:`bool`, optional): True, if the bot can delete messages + sent by the bot. + can_delete_all_messages (:obj:`bool`, optional): True, if the bot can delete all private + messages in managed chats. + can_edit_name (:obj:`bool`, optional): True, if the bot can edit the first and last name + of the business account. + can_edit_bio (:obj:`bool`, optional): True, if the bot can edit the bio of the + business account. + can_edit_profile_photo (:obj:`bool`, optional): True, if the bot can edit the profile + photo of the business account. + can_edit_username (:obj:`bool`, optional): True, if the bot can edit the username of the + business account. + can_change_gift_settings (:obj:`bool`, optional): True, if the bot can change the privacy + settings pertaining to gifts for the business account. + can_view_gifts_and_stars (:obj:`bool`, optional): True, if the bot can view gifts and the + amount of Telegram Stars owned by the business account. + can_convert_gifts_to_stars (:obj:`bool`, optional): True, if the bot can convert regular + gifts owned by the business account to Telegram Stars. + can_transfer_and_upgrade_gifts (:obj:`bool`, optional): True, if the bot can transfer and + upgrade gifts owned by the business account. + can_transfer_stars (:obj:`bool`, optional): True, if the bot can transfer Telegram Stars + received by the business account to its own account, or use them to upgrade and + transfer gifts. + can_manage_stories (:obj:`bool`, optional): True, if the bot can post, edit and delete + stories on behalf of the business account. + + Attributes: + can_reply (:obj:`bool`): Optional. True, if the bot can send and edit messages in the + private chats that had incoming messages in the last 24 hours. + can_read_messages (:obj:`bool`): Optional. True, if the bot can mark incoming private + messages as read. + can_delete_outgoing_messages (:obj:`bool`): Optional. True, if the bot can delete messages + sent by the bot. + can_delete_all_messages (:obj:`bool`): Optional. True, if the bot can delete all private + messages in managed chats. + can_edit_name (:obj:`bool`): Optional. True, if the bot can edit the first and last name + of the business account. + can_edit_bio (:obj:`bool`): Optional. True, if the bot can edit the bio of the + business account. + can_edit_profile_photo (:obj:`bool`): Optional. True, if the bot can edit the profile + photo of the business account. + can_edit_username (:obj:`bool`): Optional. True, if the bot can edit the username of the + business account. + can_change_gift_settings (:obj:`bool`): Optional. True, if the bot can change the privacy + settings pertaining to gifts for the business account. + can_view_gifts_and_stars (:obj:`bool`): Optional. True, if the bot can view gifts and the + amount of Telegram Stars owned by the business account. + can_convert_gifts_to_stars (:obj:`bool`): Optional. True, if the bot can convert regular + gifts owned by the business account to Telegram Stars. + can_transfer_and_upgrade_gifts (:obj:`bool`): Optional. True, if the bot can transfer and + upgrade gifts owned by the business account. + can_transfer_stars (:obj:`bool`): Optional. True, if the bot can transfer Telegram Stars + received by the business account to its own account, or use them to upgrade and + transfer gifts. + can_manage_stories (:obj:`bool`): Optional. True, if the bot can post, edit and delete + stories on behalf of the business account. + """ + + __slots__ = ( + "can_change_gift_settings", + "can_convert_gifts_to_stars", + "can_delete_all_messages", + "can_delete_outgoing_messages", + "can_edit_bio", + "can_edit_name", + "can_edit_profile_photo", + "can_edit_username", + "can_manage_stories", + "can_read_messages", + "can_reply", + "can_transfer_and_upgrade_gifts", + "can_transfer_stars", + "can_view_gifts_and_stars", + ) + + def __init__( + self, + can_reply: Optional[bool] = None, + can_read_messages: Optional[bool] = None, + can_delete_outgoing_messages: Optional[bool] = None, + can_delete_all_messages: Optional[bool] = None, + can_edit_name: Optional[bool] = None, + can_edit_bio: Optional[bool] = None, + can_edit_profile_photo: Optional[bool] = None, + can_edit_username: Optional[bool] = None, + can_change_gift_settings: Optional[bool] = None, + can_view_gifts_and_stars: Optional[bool] = None, + can_convert_gifts_to_stars: Optional[bool] = None, + can_transfer_and_upgrade_gifts: Optional[bool] = None, + can_transfer_stars: Optional[bool] = None, + can_manage_stories: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.can_reply: Optional[bool] = can_reply + self.can_read_messages: Optional[bool] = can_read_messages + self.can_delete_outgoing_messages: Optional[bool] = can_delete_outgoing_messages + self.can_delete_all_messages: Optional[bool] = can_delete_all_messages + self.can_edit_name: Optional[bool] = can_edit_name + self.can_edit_bio: Optional[bool] = can_edit_bio + self.can_edit_profile_photo: Optional[bool] = can_edit_profile_photo + self.can_edit_username: Optional[bool] = can_edit_username + self.can_change_gift_settings: Optional[bool] = can_change_gift_settings + self.can_view_gifts_and_stars: Optional[bool] = can_view_gifts_and_stars + self.can_convert_gifts_to_stars: Optional[bool] = can_convert_gifts_to_stars + self.can_transfer_and_upgrade_gifts: Optional[bool] = can_transfer_and_upgrade_gifts + self.can_transfer_stars: Optional[bool] = can_transfer_stars + self.can_manage_stories: Optional[bool] = can_manage_stories + + self._id_attrs = ( + self.can_reply, + self.can_read_messages, + self.can_delete_outgoing_messages, + self.can_delete_all_messages, + self.can_edit_name, + self.can_edit_bio, + self.can_edit_profile_photo, + self.can_edit_username, + self.can_change_gift_settings, + self.can_view_gifts_and_stars, + self.can_convert_gifts_to_stars, + self.can_transfer_and_upgrade_gifts, + self.can_transfer_stars, + self.can_manage_stories, + ) + + self._freeze() + + class BusinessConnection(TelegramObject): """ Describes the connection of the bot with a business account. Objects of this class are comparable in terms of equality. Two objects of this class are considered equal if their :attr:`id`, :attr:`user`, :attr:`user_chat_id`, :attr:`date`, - :attr:`can_reply`, and :attr:`is_enabled` are equal. + :attr:`rights`, and :attr:`is_enabled` are equal. .. versionadded:: 21.1 + .. versionchanged:: NEXT.VERSION + Equality comparison now considers :attr:`rights` instead of :attr:`can_reply`. Args: id (:obj:`str`): Unique identifier of the business connection. @@ -53,7 +200,13 @@ class BusinessConnection(TelegramObject): date (:obj:`datetime.datetime`): Date the connection was established in Unix time. can_reply (:obj:`bool`): True, if the bot can act on behalf of the business account in chats that were active in the last 24 hours. + + .. deprecated:: NEXT.VERSION + Bot API 9.0 deprecated this argument in favor of :paramref:`rights` is_enabled (:obj:`bool`): True, if the connection is active. + rights (:class:`BusinessBotRights`, optional): Rights of the business bot. + + .. versionadded:: NEXT.VERSION Attributes: id (:obj:`str`): Unique identifier of the business connection. @@ -61,16 +214,18 @@ class BusinessConnection(TelegramObject): user_chat_id (:obj:`int`): Identifier of a private chat with the user who created the business connection. date (:obj:`datetime.datetime`): Date the connection was established in Unix time. - can_reply (:obj:`bool`): True, if the bot can act on behalf of the business account in - chats that were active in the last 24 hours. is_enabled (:obj:`bool`): True, if the connection is active. + rights (:class:`BusinessBotRights`): Optional. Rights of the business bot. + + .. versionadded:: NEXT.VERSION """ __slots__ = ( - "can_reply", + "_can_reply", "date", "id", "is_enabled", + "rights", "user", "user_chat_id", ) @@ -83,6 +238,7 @@ def __init__( date: dtm.datetime, can_reply: bool, is_enabled: bool, + rights: Optional[BusinessBotRights] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -91,20 +247,37 @@ def __init__( self.user: User = user self.user_chat_id: int = user_chat_id self.date: dtm.datetime = date - self.can_reply: bool = can_reply + self._can_reply: bool = can_reply self.is_enabled: bool = is_enabled + self.rights: Optional[BusinessBotRights] = rights self._id_attrs = ( self.id, self.user, self.user_chat_id, self.date, - self.can_reply, + self.rights, self.is_enabled, ) self._freeze() + @property + def can_reply(self) -> bool: + """:obj:`bool`: True, if the bot can act on behalf of the business account in + chats that were active in the last 24 hours. + + .. deprecated:: NEXT.VERSION + Bot API 9.0 deprecated this argument in favor of :attr:`rights` + """ + warn_about_deprecated_attr_in_property( + deprecated_attr_name="can_reply", + new_attr_name="rights", + bot_api_version="9.0", + ptb_version="NEXT.VERSION", + ) + return self._can_reply + @classmethod def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BusinessConnection": """See :meth:`telegram.TelegramObject.de_json`.""" @@ -115,6 +288,7 @@ def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BusinessConnec data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo) data["user"] = de_json_optional(data.get("user"), User, bot) + data["rights"] = de_json_optional(data.get("rights"), BusinessBotRights, bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_utils/warnings_transition.py b/telegram/_utils/warnings_transition.py index cd9fecd7562..7aca62c2ac6 100644 --- a/telegram/_utils/warnings_transition.py +++ b/telegram/_utils/warnings_transition.py @@ -35,12 +35,12 @@ def build_deprecation_warning_message( object_type: str, bot_api_version: str, ) -> str: - """Builds a warning message for the transition in API when an object is renamed. + """Builds a warning message for the transition in API when an object is renamed/replaced. Returns a warning message that can be used in `warn` function. """ return ( - f"The {object_type} '{deprecated_name}' was renamed to '{new_name}' in Bot API " + f"The {object_type} '{deprecated_name}' was replaced by '{new_name}' in Bot API " f"{bot_api_version}. We recommend using '{new_name}' instead of " f"'{deprecated_name}'." ) diff --git a/tests/test_business_classes.py b/tests/test_business_classes.py index d8e976a9144..27241b6e8ba 100644 --- a/tests/test_business_classes.py +++ b/tests/test_business_classes.py @@ -32,7 +32,9 @@ Sticker, User, ) +from telegram._business import BusinessBotRights from telegram._utils.datetime import UTC, to_timestamp +from telegram.warnings import PTBDeprecationWarning from tests.auxil.slots import mro_slots @@ -41,7 +43,20 @@ class BusinessTestBase: user = User(123, "test_user", False) user_chat_id = 123 date = dtm.datetime.now(tz=UTC).replace(microsecond=0) + can_change_gift_settings = True + can_convert_gifts_to_stars = True + can_delete_all_messages = True + can_delete_outgoing_messages = True + can_edit_bio = True + can_edit_name = True + can_edit_profile_photo = True + can_edit_username = True + can_manage_stories = True + can_read_messages = True can_reply = True + can_transfer_and_upgrade_gifts = True + can_transfer_stars = True + can_view_gifts_and_stars = True is_enabled = True message_ids = (123, 321) business_connection_id = "123" @@ -60,7 +75,27 @@ class BusinessTestBase: @pytest.fixture(scope="module") -def business_connection(): +def business_bot_rights(): + return BusinessBotRights( + can_change_gift_settings=BusinessTestBase.can_change_gift_settings, + can_convert_gifts_to_stars=BusinessTestBase.can_convert_gifts_to_stars, + can_delete_all_messages=BusinessTestBase.can_delete_all_messages, + can_delete_outgoing_messages=BusinessTestBase.can_delete_outgoing_messages, + can_edit_bio=BusinessTestBase.can_edit_bio, + can_edit_name=BusinessTestBase.can_edit_name, + can_edit_profile_photo=BusinessTestBase.can_edit_profile_photo, + can_edit_username=BusinessTestBase.can_edit_username, + can_manage_stories=BusinessTestBase.can_manage_stories, + can_read_messages=BusinessTestBase.can_read_messages, + can_reply=BusinessTestBase.can_reply, + can_transfer_and_upgrade_gifts=BusinessTestBase.can_transfer_and_upgrade_gifts, + can_transfer_stars=BusinessTestBase.can_transfer_stars, + can_view_gifts_and_stars=BusinessTestBase.can_view_gifts_and_stars, + ) + + +@pytest.fixture(scope="module") +def business_connection(business_bot_rights): return BusinessConnection( BusinessTestBase.id_, BusinessTestBase.user, @@ -68,6 +103,7 @@ def business_connection(): BusinessTestBase.date, BusinessTestBase.can_reply, BusinessTestBase.is_enabled, + rights=business_bot_rights, ) @@ -113,6 +149,90 @@ def business_opening_hours(): ) +class TestBusinessBotRightsWithoutRequest(BusinessTestBase): + def test_slot_behaviour(self, business_bot_rights): + inst = business_bot_rights + for attr in inst.__slots__: + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" + + def test_to_dict(self, business_bot_rights): + rights_dict = business_bot_rights.to_dict() + + assert isinstance(rights_dict, dict) + assert rights_dict["can_reply"] is self.can_reply + assert rights_dict["can_read_messages"] is self.can_read_messages + assert rights_dict["can_delete_outgoing_messages"] is self.can_delete_outgoing_messages + assert rights_dict["can_delete_all_messages"] is self.can_delete_all_messages + assert rights_dict["can_edit_name"] is self.can_edit_name + assert rights_dict["can_edit_bio"] is self.can_edit_bio + assert rights_dict["can_edit_profile_photo"] is self.can_edit_profile_photo + assert rights_dict["can_edit_username"] is self.can_edit_username + assert rights_dict["can_change_gift_settings"] is self.can_change_gift_settings + assert rights_dict["can_view_gifts_and_stars"] is self.can_view_gifts_and_stars + assert rights_dict["can_convert_gifts_to_stars"] is self.can_convert_gifts_to_stars + assert rights_dict["can_transfer_and_upgrade_gifts"] is self.can_transfer_and_upgrade_gifts + assert rights_dict["can_transfer_stars"] is self.can_transfer_stars + assert rights_dict["can_manage_stories"] is self.can_manage_stories + + def test_de_json(self): + json_dict = { + "can_reply": self.can_reply, + "can_read_messages": self.can_read_messages, + "can_delete_outgoing_messages": self.can_delete_outgoing_messages, + "can_delete_all_messages": self.can_delete_all_messages, + "can_edit_name": self.can_edit_name, + "can_edit_bio": self.can_edit_bio, + "can_edit_profile_photo": self.can_edit_profile_photo, + "can_edit_username": self.can_edit_username, + "can_change_gift_settings": self.can_change_gift_settings, + "can_view_gifts_and_stars": self.can_view_gifts_and_stars, + "can_convert_gifts_to_stars": self.can_convert_gifts_to_stars, + "can_transfer_and_upgrade_gifts": self.can_transfer_and_upgrade_gifts, + "can_transfer_stars": self.can_transfer_stars, + "can_manage_stories": self.can_manage_stories, + } + + rights = BusinessBotRights.de_json(json_dict, None) + assert rights.can_reply is self.can_reply + assert rights.can_read_messages is self.can_read_messages + assert rights.can_delete_outgoing_messages is self.can_delete_outgoing_messages + assert rights.can_delete_all_messages is self.can_delete_all_messages + assert rights.can_edit_name is self.can_edit_name + assert rights.can_edit_bio is self.can_edit_bio + assert rights.can_edit_profile_photo is self.can_edit_profile_photo + assert rights.can_edit_username is self.can_edit_username + assert rights.can_change_gift_settings is self.can_change_gift_settings + assert rights.can_view_gifts_and_stars is self.can_view_gifts_and_stars + assert rights.can_convert_gifts_to_stars is self.can_convert_gifts_to_stars + assert rights.can_transfer_and_upgrade_gifts is self.can_transfer_and_upgrade_gifts + assert rights.can_transfer_stars is self.can_transfer_stars + assert rights.can_manage_stories is self.can_manage_stories + assert rights.api_kwargs == {} + assert isinstance(rights, BusinessBotRights) + + def test_equality(self): + rights1 = BusinessBotRights( + can_reply=self.can_reply, + ) + + rights2 = BusinessBotRights( + can_reply=True, + ) + + rights3 = BusinessBotRights( + can_reply=True, + can_read_messages=self.can_read_messages, + ) + + assert rights1 == rights2 + assert hash(rights1) == hash(rights2) + assert rights1 is not rights2 + + assert rights1 != rights3 + assert hash(rights1) != hash(rights3) + + class TestBusinessConnectionWithoutRequest(BusinessTestBase): def test_slots(self, business_connection): bc = business_connection @@ -120,7 +240,7 @@ def test_slots(self, business_connection): assert getattr(bc, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(bc)) == len(set(mro_slots(bc))), "duplicate slot" - def test_de_json(self): + def test_de_json(self, business_bot_rights): json_dict = { "id": self.id_, "user": self.user.to_dict(), @@ -128,6 +248,7 @@ def test_de_json(self): "date": to_timestamp(self.date), "can_reply": self.can_reply, "is_enabled": self.is_enabled, + "rights": business_bot_rights.to_dict(), } bc = BusinessConnection.de_json(json_dict, None) assert bc.id == self.id_ @@ -136,10 +257,11 @@ def test_de_json(self): assert bc.date == self.date assert bc.can_reply == self.can_reply assert bc.is_enabled == self.is_enabled + assert bc.rights == business_bot_rights assert bc.api_kwargs == {} assert isinstance(bc, BusinessConnection) - def test_de_json_localization(self, offline_bot, raw_bot, tz_bot): + def test_de_json_localization(self, offline_bot, raw_bot, tz_bot, business_bot_rights): json_dict = { "id": self.id_, "user": self.user.to_dict(), @@ -147,6 +269,7 @@ def test_de_json_localization(self, offline_bot, raw_bot, tz_bot): "date": to_timestamp(self.date), "can_reply": self.can_reply, "is_enabled": self.is_enabled, + "rights": business_bot_rights.to_dict(), } chat_bot = BusinessConnection.de_json(json_dict, offline_bot) chat_bot_raw = BusinessConnection.de_json(json_dict, raw_bot) @@ -160,25 +283,52 @@ def test_de_json_localization(self, offline_bot, raw_bot, tz_bot): assert chat_bot_raw.date.tzinfo == UTC assert date_offset_tz == date_offset - def test_to_dict(self, business_connection): + def test_to_dict(self, business_connection, business_bot_rights): bc_dict = business_connection.to_dict() assert isinstance(bc_dict, dict) assert bc_dict["id"] == self.id_ assert bc_dict["user"] == self.user.to_dict() assert bc_dict["user_chat_id"] == self.user_chat_id assert bc_dict["date"] == to_timestamp(self.date) - assert bc_dict["can_reply"] == self.can_reply assert bc_dict["is_enabled"] == self.is_enabled + assert bc_dict["rights"] == business_bot_rights.to_dict() - def test_equality(self): + def test_equality(self, business_bot_rights): bc1 = BusinessConnection( - self.id_, self.user, self.user_chat_id, self.date, self.can_reply, self.is_enabled + self.id_, + self.user, + self.user_chat_id, + self.date, + self.can_reply, + self.is_enabled, + rights=business_bot_rights, ) bc2 = BusinessConnection( - self.id_, self.user, self.user_chat_id, self.date, self.can_reply, self.is_enabled + self.id_, + self.user, + self.user_chat_id, + self.date, + self.can_reply, + self.is_enabled, + rights=business_bot_rights, ) bc3 = BusinessConnection( - "321", self.user, self.user_chat_id, self.date, self.can_reply, self.is_enabled + "321", + self.user, + self.user_chat_id, + self.date, + self.can_reply, + self.is_enabled, + rights=business_bot_rights, + ) + bc4 = BusinessConnection( + self.id_, + self.user, + self.user_chat_id, + self.date, + self.can_reply, + self.is_enabled, + rights=BusinessBotRights(), ) assert bc1 == bc2 @@ -187,6 +337,16 @@ def test_equality(self): assert bc1 != bc3 assert hash(bc1) != hash(bc3) + assert bc1 != bc4 + assert hash(bc1) != hash(bc4) + + def test_can_reply_property_deprecation(self, business_connection): + with pytest.warns(PTBDeprecationWarning, match=r"9\.0.*can\_reply") as record: + assert business_connection.can_reply is self.can_reply + + assert record[0].category == PTBDeprecationWarning + assert record[0].filename == __file__, "wrong stacklevel!" + class TestBusinessMessagesDeleted(BusinessTestBase): def test_slots(self, business_messages_deleted): From 7f2327a4716ea67f13105fdee63e246f1f96771d Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:29:05 +0200 Subject: [PATCH 2/5] add links in bot methods --- telegram/_bot.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 6e706ce0953..a45de32d14f 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -9415,7 +9415,7 @@ async def read_business_message( ) -> bool: """ Marks incoming message as read on behalf of a business account. - Requires the ``can_read_messages`` business bot right. + Requires the :attr:`~telegram.BusinessBotRights.can_read_messages` business bot right. .. versionadded:: NEXT.VERSION @@ -9462,8 +9462,10 @@ async def delete_business_messages( ) -> bool: """ Delete messages on behalf of a business account. Requires the - ``can_delete_outgoing_messages`` business bot right to delete messages sent by the bot - itself, or the ``can_delete_all_messages`` business bot right to delete any message. + :attr:`~telegram.BusinessBotRights.can_delete_outgoing_messages` business bot right to + delete messages sent by the bot itself, or the + :attr:`~telegram.BusinessBotRights.can_delete_all_messages` business bot right to delete + any message. .. versionadded:: NEXT.VERSION @@ -9510,7 +9512,7 @@ async def set_business_account_name( ) -> bool: """ Changes the first and last name of a managed business account. Requires the - ``can_change_name`` business bot right. + :attr:`~telegram.BusinessBotRights.can_change_name` business bot right. .. versionadded:: NEXT.VERSION @@ -9557,7 +9559,7 @@ async def set_business_account_username( ) -> bool: """ Changes the username of a managed business account. Requires the - ``can_change_username`` business bot right. + :attr:`~telegram.BusinessBotRights.can_change_username` business bot right. .. versionadded:: NEXT.VERSION @@ -9598,8 +9600,8 @@ async def set_business_account_bio( api_kwargs: Optional[JSONDict] = None, ) -> bool: """ - Changes the bio of a managed business account. Requires the ``can_change_bio`` business - bot right. + Changes the bio of a managed business account. Requires the + :attr:`~telegram.BusinessBotRights.can_change_bio` business bot right. .. versionadded:: NEXT.VERSION From ad2464023e640313375f7b7d0c546bf1eb64425e Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 16 Apr 2025 19:33:12 +0000 Subject: [PATCH 3/5] Add chango fragment for PR #4759 --- changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml diff --git a/changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml b/changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml new file mode 100644 index 00000000000..ab6a1b06de1 --- /dev/null +++ b/changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml @@ -0,0 +1,5 @@ +features = "Api 9.0 business bot rights" +[[pull_requests]] +uid = "4759" +author_uid = "Bibo-Joshi" +closes_threads = [] From 63746645d72231721ce6e2d3c1f53babf126510a Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:34:44 +0200 Subject: [PATCH 4/5] update chango fragments --- changes/unreleased/4756.JT5nmUmGRG6qDEh5ScMn5f.toml | 4 ++++ changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml diff --git a/changes/unreleased/4756.JT5nmUmGRG6qDEh5ScMn5f.toml b/changes/unreleased/4756.JT5nmUmGRG6qDEh5ScMn5f.toml index 219c3412640..32dee0a580b 100644 --- a/changes/unreleased/4756.JT5nmUmGRG6qDEh5ScMn5f.toml +++ b/changes/unreleased/4756.JT5nmUmGRG6qDEh5ScMn5f.toml @@ -7,3 +7,7 @@ closes_threads = ["4754"] uid = "4757" author_uid = "Bibo-Joshi" closes_threads = [] +[[pull_requests]] +uid = "4759" +author_uid = "Bibo-Joshi" +closes_threads = [] diff --git a/changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml b/changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml deleted file mode 100644 index ab6a1b06de1..00000000000 --- a/changes/unreleased/4759.kDZjXjFhpyxGpGEjiofGsj.toml +++ /dev/null @@ -1,5 +0,0 @@ -features = "Api 9.0 business bot rights" -[[pull_requests]] -uid = "4759" -author_uid = "Bibo-Joshi" -closes_threads = [] From c509b928ea80c552ffb5a170a6227b2a39d0c8d6 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:58:42 +0200 Subject: [PATCH 5/5] Deprecate can_reply argument, add warning, update test_official exceptions --- telegram/_business.py | 42 ++++++++++++++++++++++++------- tests/test_business_classes.py | 21 +++++++++++++++- tests/test_official/exceptions.py | 6 +++++ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/telegram/_business.py b/telegram/_business.py index 7d006016631..453e6b240de 100644 --- a/telegram/_business.py +++ b/telegram/_business.py @@ -30,7 +30,12 @@ from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp from telegram._utils.types import JSONDict -from telegram._utils.warnings_transition import warn_about_deprecated_attr_in_property +from telegram._utils.warnings import warn +from telegram._utils.warnings_transition import ( + build_deprecation_warning_message, + warn_about_deprecated_attr_in_property, +) +from telegram.warnings import PTBDeprecationWarning if TYPE_CHECKING: from telegram import Bot @@ -198,11 +203,11 @@ class BusinessConnection(TelegramObject): user_chat_id (:obj:`int`): Identifier of a private chat with the user who created the business connection. date (:obj:`datetime.datetime`): Date the connection was established in Unix time. - can_reply (:obj:`bool`): True, if the bot can act on behalf of the business account in - chats that were active in the last 24 hours. + can_reply (:obj:`bool`, optional): True, if the bot can act on behalf of the business + account in chats that were active in the last 24 hours. .. deprecated:: NEXT.VERSION - Bot API 9.0 deprecated this argument in favor of :paramref:`rights` + Bot API 9.0 deprecated this argument in favor of :paramref:`rights`. is_enabled (:obj:`bool`): True, if the connection is active. rights (:class:`BusinessBotRights`, optional): Rights of the business bot. @@ -236,18 +241,37 @@ def __init__( user: "User", user_chat_id: int, date: dtm.datetime, - can_reply: bool, - is_enabled: bool, + can_reply: Optional[bool] = None, + # temporarily optional to account for changed signature + # tags: deprecated NEXT.VERSION; bot api 9.0 + is_enabled: Optional[bool] = None, rights: Optional[BusinessBotRights] = None, *, api_kwargs: Optional[JSONDict] = None, ): + if is_enabled is None: + raise TypeError("Missing required argument `is_enabled`") + + if can_reply is not None: + warn( + PTBDeprecationWarning( + version="NEXT.VERSION", + message=build_deprecation_warning_message( + deprecated_name="can_reply", + new_name="rights", + bot_api_version="9.0", + object_type="parameter", + ), + ), + stacklevel=2, + ) + super().__init__(api_kwargs=api_kwargs) self.id: str = id self.user: User = user self.user_chat_id: int = user_chat_id self.date: dtm.datetime = date - self._can_reply: bool = can_reply + self._can_reply: Optional[bool] = can_reply self.is_enabled: bool = is_enabled self.rights: Optional[BusinessBotRights] = rights @@ -263,8 +287,8 @@ def __init__( self._freeze() @property - def can_reply(self) -> bool: - """:obj:`bool`: True, if the bot can act on behalf of the business account in + def can_reply(self) -> Optional[bool]: + """:obj:`bool`: Optional. True, if the bot can act on behalf of the business account in chats that were active in the last 24 hours. .. deprecated:: NEXT.VERSION diff --git a/tests/test_business_classes.py b/tests/test_business_classes.py index 27241b6e8ba..e46c22f5ab5 100644 --- a/tests/test_business_classes.py +++ b/tests/test_business_classes.py @@ -340,13 +340,32 @@ def test_equality(self, business_bot_rights): assert bc1 != bc4 assert hash(bc1) != hash(bc4) - def test_can_reply_property_deprecation(self, business_connection): + def test_can_reply_argument_property_deprecation(self, business_connection): + with pytest.warns(PTBDeprecationWarning, match=r"9\.0.*can\_reply") as record: + assert BusinessConnection( + id=self.id_, + user=self.user, + user_chat_id=self.user_chat_id, + date=self.date, + can_reply=True, + is_enabled=self.is_enabled, + ) + + assert record[0].category == PTBDeprecationWarning + assert record[0].filename == __file__, "wrong stacklevel!" + with pytest.warns(PTBDeprecationWarning, match=r"9\.0.*can\_reply") as record: assert business_connection.can_reply is self.can_reply assert record[0].category == PTBDeprecationWarning assert record[0].filename == __file__, "wrong stacklevel!" + def test_is_enabled_remains_required(self): + with pytest.raises(TypeError): + BusinessConnection( + id=self.id_, user=self.user, user_chat_id=self.user_chat_id, date=self.date + ) + class TestBusinessMessagesDeleted(BusinessTestBase): def test_slots(self, business_messages_deleted): diff --git a/tests/test_official/exceptions.py b/tests/test_official/exceptions.py index d0d73fa4b7b..c4a011808de 100644 --- a/tests/test_official/exceptions.py +++ b/tests/test_official/exceptions.py @@ -149,6 +149,9 @@ class ParamTypeCheckingExceptions: "TransactionPartner": {"type"}, # attributes common to all subclasses "PaidMedia": {"type"}, # attributes common to all subclasses "InputPaidMedia": {"type", "media"}, # attributes common to all subclasses + # backwards compatibility for api 9.0 changes + # tags: deprecated NEXT.VERSION, bot api 9.0 + "BusinessConnection": {"can_reply"}, } @@ -192,6 +195,9 @@ def ptb_ignored_params(object_name: str) -> set[str]: "send_venue": {"latitude", "longitude", "title", "address"}, "send_contact": {"phone_number", "first_name"}, # ----> + # backwards compatibility for api 9.0 changes + # tags: deprecated NEXT.VERSION, bot api 9.0 + "BusinessConnection": {"is_enabled"}, }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: