From d4fda6a8f1c2d28a7db1bda1ff8005a52ef8b701 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Thu, 9 May 2024 03:07:06 +0300 Subject: [PATCH 01/12] Add `ChatMemberUpdated.via_join_request` and tests - [x] Added the field via_join_request to the class ChatMemberUpdated. --- telegram/_chatmemberupdated.py | 13 +++++++++++++ tests/test_chatmemberupdated.py | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index 7d5ee556be7..41dbb884492 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -63,6 +63,11 @@ class ChatMemberUpdated(TelegramObject): chat via a chat folder invite link .. versionadded:: 20.3 + via_join_request (:obj:`bool`, optional): :obj:`True`, if the user joined the chat after + sending a direct join request without using an invite link and being approved by + an administrator + + .. versionadded:: NEXT.VERSION Attributes: chat (:class:`telegram.Chat`): Chat the user belongs to. @@ -80,6 +85,11 @@ class ChatMemberUpdated(TelegramObject): chat via a chat folder invite link .. versionadded:: 20.3 + via_join_request (:obj:`bool`): Optional. :obj:`True`, if the user joined the chat after + sending a direct join request without using an invite link and being approved + by an administrator + + .. versionadded:: NEXT.VERSION """ @@ -91,6 +101,7 @@ class ChatMemberUpdated(TelegramObject): "new_chat_member", "old_chat_member", "via_chat_folder_invite_link", + "via_join_request", ) def __init__( @@ -102,6 +113,7 @@ def __init__( new_chat_member: ChatMember, invite_link: Optional[ChatInviteLink] = None, via_chat_folder_invite_link: Optional[bool] = None, + via_join_request: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -116,6 +128,7 @@ def __init__( # Optionals self.invite_link: Optional[ChatInviteLink] = invite_link + self.via_join_request: Optional[bool] = via_join_request self._id_attrs = ( self.chat, diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py index 0cf5e58101c..0efbcd8d0ab 100644 --- a/tests/test_chatmemberupdated.py +++ b/tests/test_chatmemberupdated.py @@ -82,7 +82,9 @@ def invite_link(user): @pytest.fixture(scope="module") def chat_member_updated(user, chat, old_chat_member, new_chat_member, invite_link, time): - return ChatMemberUpdated(chat, user, time, old_chat_member, new_chat_member, invite_link, True) + return ChatMemberUpdated( + chat, user, time, old_chat_member, new_chat_member, invite_link, True, True + ) class TestChatMemberUpdatedBase: @@ -129,6 +131,7 @@ def test_de_json_all_args( "new_chat_member": new_chat_member.to_dict(), "invite_link": invite_link.to_dict(), "via_chat_folder_invite_link": True, + "via_join_request": True, } chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot) @@ -142,6 +145,7 @@ def test_de_json_all_args( assert chat_member_updated.new_chat_member == new_chat_member assert chat_member_updated.invite_link == invite_link assert chat_member_updated.via_chat_folder_invite_link is True + assert chat_member_updated.via_join_request is True def test_de_json_localization( self, bot, raw_bot, tz_bot, user, chat, old_chat_member, new_chat_member, time, invite_link @@ -188,6 +192,7 @@ def test_to_dict(self, chat_member_updated): chat_member_updated_dict["via_chat_folder_invite_link"] == chat_member_updated.via_chat_folder_invite_link ) + assert chat_member_updated_dict["via_join_request"] == chat_member_updated.via_join_request def test_equality(self, time, old_chat_member, new_chat_member, invite_link): a = ChatMemberUpdated( From 2e94c7b3215e5b705c6c7ea1a22f0a1e6f637f99 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Thu, 9 May 2024 04:37:25 +0300 Subject: [PATCH 02/12] Add `edit_message_live_location.live_period` and tests. - [x] Added the parameter live_period to the method editMessageLiveLocation. --- telegram/_bot.py | 10 ++++++++++ telegram/_callbackquery.py | 3 +++ telegram/_message.py | 2 ++ telegram/ext/_extbot.py | 2 ++ tests/_files/test_location.py | 6 +++++- tests/test_callbackquery.py | 9 ++++++--- tests/test_message.py | 5 +++-- 7 files changed, 31 insertions(+), 6 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 8bb4af23de7..937d186ff01 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -2720,6 +2720,7 @@ async def edit_message_live_location( horizontal_accuracy: Optional[float] = None, heading: Optional[int] = None, proximity_alert_radius: Optional[int] = None, + live_period: Optional[int] = None, *, location: Optional[Location] = None, read_timeout: ODVInput[float] = DEFAULT_NONE, @@ -2758,6 +2759,14 @@ async def edit_message_live_location( if specified. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for a new inline keyboard. + live_period (:obj:`int`, optional): New period in seconds during which the location + can be updated, starting from the message send date. If `0x7FFFFFFF` is specified, + then the location can be updated forever. Otherwise, the new value must not exceed + the current live_period by more than a day, and the live location expiration date + must remain within the next 90 days. If not specified, then `live_period` + remains unchanged + + .. versionadded:: NEXT.VERSION. Keyword Args: location (:class:`telegram.Location`, optional): The location to send. @@ -2790,6 +2799,7 @@ async def edit_message_live_location( "horizontal_accuracy": horizontal_accuracy, "heading": heading, "proximity_alert_radius": proximity_alert_radius, + "live_period": live_period, } return await self._send_message( diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 8fbd0013a4e..3df7089c997 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -461,6 +461,7 @@ async def edit_message_live_location( horizontal_accuracy: Optional[float] = None, heading: Optional[int] = None, proximity_alert_radius: Optional[int] = None, + live_period: Optional[int] = None, *, location: Optional[Location] = None, read_timeout: ODVInput[float] = DEFAULT_NONE, @@ -509,6 +510,7 @@ async def edit_message_live_location( horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, + live_period=live_period, chat_id=None, message_id=None, ) @@ -525,6 +527,7 @@ async def edit_message_live_location( horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, + live_period=live_period, ) async def stop_message_live_location( diff --git a/telegram/_message.py b/telegram/_message.py index 87ecdd300f3..f48418a8f8f 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -3653,6 +3653,7 @@ async def edit_live_location( horizontal_accuracy: Optional[float] = None, heading: Optional[int] = None, proximity_alert_radius: Optional[int] = None, + live_period: Optional[int] = None, *, location: Optional[Location] = None, read_timeout: ODVInput[float] = DEFAULT_NONE, @@ -3694,6 +3695,7 @@ async def edit_live_location( horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, + live_period=live_period, inline_message_id=None, ) diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 7b5649ebea3..843aebec2a4 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -1522,6 +1522,7 @@ async def edit_message_live_location( horizontal_accuracy: Optional[float] = None, heading: Optional[int] = None, proximity_alert_radius: Optional[int] = None, + live_period: Optional[int] = None, *, location: Optional[Location] = None, read_timeout: ODVInput[float] = DEFAULT_NONE, @@ -1541,6 +1542,7 @@ async def edit_message_live_location( horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, + live_period=live_period, location=location, read_timeout=read_timeout, write_timeout=write_timeout, diff --git a/tests/_files/test_location.py b/tests/_files/test_location.py index aec282ccdcd..5b94df4916b 100644 --- a/tests/_files/test_location.py +++ b/tests/_files/test_location.py @@ -124,7 +124,8 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): ha = data["horizontal_accuracy"] == "50" heading = data["heading"] == "90" prox_alert = data["proximity_alert_radius"] == "1000" - return lat and lon and id_ and ha and heading and prox_alert + live = data["live_period"] == "900" + return lat and lon and id_ and ha and heading and prox_alert and live monkeypatch.setattr(bot.request, "post", make_assertion) assert await bot.edit_message_live_location( @@ -133,6 +134,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): horizontal_accuracy=50, heading=90, proximity_alert_radius=1000, + live_period=900, ) # TODO: Needs improvement with in inline sent live location. @@ -262,6 +264,7 @@ async def test_send_live_location(self, bot, chat_id): horizontal_accuracy=30, heading=10, proximity_alert_radius=500, + live_period=200, ) assert pytest.approx(message2.location.latitude, rel=1e-5) == 52.223098 @@ -269,6 +272,7 @@ async def test_send_live_location(self, bot, chat_id): assert message2.location.horizontal_accuracy == 30 assert message2.location.heading == 10 assert message2.location.proximity_alert_radius == 500 + assert message2.location.live_period == 200 await bot.stop_message_live_location(message.chat_id, message.message_id) with pytest.raises(BadRequest, match="Message can't be edited"): diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py index 1db05e4b973..66dc6856924 100644 --- a/tests/test_callbackquery.py +++ b/tests/test_callbackquery.py @@ -301,8 +301,9 @@ async def test_edit_message_live_location(self, monkeypatch, callback_query): async def make_assertion(*_, **kwargs): latitude = kwargs.get("latitude") == 1 longitude = kwargs.get("longitude") == 2 + live = kwargs.get("live_period") == 900 ids = self.check_passed_ids(callback_query, kwargs) - return ids and latitude and longitude + return ids and latitude and longitude and live assert check_shortcut_signature( CallbackQuery.edit_message_live_location, @@ -322,8 +323,10 @@ async def make_assertion(*_, **kwargs): ) monkeypatch.setattr(callback_query.get_bot(), "edit_message_live_location", make_assertion) - assert await callback_query.edit_message_live_location(latitude=1, longitude=2) - assert await callback_query.edit_message_live_location(1, 2) + assert await callback_query.edit_message_live_location( + latitude=1, longitude=2, live_period=900 + ) + assert await callback_query.edit_message_live_location(1, 2, live_period=900) async def test_stop_message_live_location(self, monkeypatch, callback_query): if isinstance(callback_query.message, InaccessibleMessage): diff --git a/tests/test_message.py b/tests/test_message.py index e70b8f0668f..440ec50651c 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -2414,7 +2414,8 @@ async def make_assertion(*_, **kwargs): message_id = kwargs["message_id"] == message.message_id latitude = kwargs["latitude"] == 1 longitude = kwargs["longitude"] == 2 - return chat_id and message_id and longitude and latitude + live = kwargs["live_period"] == 900 + return chat_id and message_id and longitude and latitude and live assert check_shortcut_signature( Message.edit_live_location, @@ -2432,7 +2433,7 @@ async def make_assertion(*_, **kwargs): assert await check_defaults_handling(message.edit_live_location, message.get_bot()) monkeypatch.setattr(message.get_bot(), "edit_message_live_location", make_assertion) - assert await message.edit_live_location(latitude=1, longitude=2) + assert await message.edit_live_location(latitude=1, longitude=2, live_period=900) async def test_stop_live_location(self, monkeypatch, message): async def make_assertion(*_, **kwargs): From 69b0c38da681f8890977b758bfdb8c08c7e1f1d2 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Thu, 9 May 2024 14:04:17 +0300 Subject: [PATCH 03/12] Add Backgrounds and tests. - [x] Added the classes `ChatBackground`, `BackgroundType`, `BackgroundFill` and the field `chat_background_set` of type `ChatBackground` to the class Message, describing service messages about background changes --- docs/source/telegram.at-tree.rst | 10 + docs/source/telegram.backgroundfill.rst | 8 + ...elegram.backgroundfillfreeformgradient.rst | 8 + .../telegram.backgroundfillgradient.rst | 8 + docs/source/telegram.backgroundfillsolid.rst | 8 + docs/source/telegram.backgroundtype.rst | 8 + .../telegram.backgroundtypechattheme.rst | 8 + docs/source/telegram.backgroundtypefill.rst | 8 + .../source/telegram.backgroundtypepattern.rst | 8 + .../telegram.backgroundtypewallpaper.rst | 8 + docs/source/telegram.chatbackground.rst | 8 + telegram/__init__.py | 22 + telegram/_chatbackground.py | 494 ++++++++++++++++++ telegram/_message.py | 15 + telegram/constants.py | 43 ++ tests/test_chatbackground.py | 345 ++++++++++++ tests/test_message.py | 4 + 17 files changed, 1013 insertions(+) create mode 100644 docs/source/telegram.backgroundfill.rst create mode 100644 docs/source/telegram.backgroundfillfreeformgradient.rst create mode 100644 docs/source/telegram.backgroundfillgradient.rst create mode 100644 docs/source/telegram.backgroundfillsolid.rst create mode 100644 docs/source/telegram.backgroundtype.rst create mode 100644 docs/source/telegram.backgroundtypechattheme.rst create mode 100644 docs/source/telegram.backgroundtypefill.rst create mode 100644 docs/source/telegram.backgroundtypepattern.rst create mode 100644 docs/source/telegram.backgroundtypewallpaper.rst create mode 100644 docs/source/telegram.chatbackground.rst create mode 100644 telegram/_chatbackground.py create mode 100644 tests/test_chatbackground.py diff --git a/docs/source/telegram.at-tree.rst b/docs/source/telegram.at-tree.rst index 3d78292588a..55478148381 100644 --- a/docs/source/telegram.at-tree.rst +++ b/docs/source/telegram.at-tree.rst @@ -28,6 +28,16 @@ Available Types telegram.callbackquery telegram.chat telegram.chatadministratorrights + telegram.chatbackground + telegram.backgroundtype + telegram.backgroundtypefill + telegram.backgroundtypewallpaper + telegram.backgroundtypepattern + telegram.backgroundtypechattheme + telegram.backgroundfill + telegram.backgroundfillsolid + telegram.backgroundfillgradient + telegram.backgroundfillfreeformgradient telegram.chatboost telegram.chatboostadded telegram.chatboostremoved diff --git a/docs/source/telegram.backgroundfill.rst b/docs/source/telegram.backgroundfill.rst new file mode 100644 index 00000000000..5f7e68f9f08 --- /dev/null +++ b/docs/source/telegram.backgroundfill.rst @@ -0,0 +1,8 @@ +BackgroundFill +======================= + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundFill + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundfillfreeformgradient.rst b/docs/source/telegram.backgroundfillfreeformgradient.rst new file mode 100644 index 00000000000..24ddffb70e5 --- /dev/null +++ b/docs/source/telegram.backgroundfillfreeformgradient.rst @@ -0,0 +1,8 @@ +BackgroundFillFreeformGradient +============================== + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundFillFreeformGradient + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundfillgradient.rst b/docs/source/telegram.backgroundfillgradient.rst new file mode 100644 index 00000000000..9955c6f1d86 --- /dev/null +++ b/docs/source/telegram.backgroundfillgradient.rst @@ -0,0 +1,8 @@ +BackgroundFillGradient +====================== + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundFillGradient + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundfillsolid.rst b/docs/source/telegram.backgroundfillsolid.rst new file mode 100644 index 00000000000..27be28d520b --- /dev/null +++ b/docs/source/telegram.backgroundfillsolid.rst @@ -0,0 +1,8 @@ +BackgroundFillSolid +=================== + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundFillSolid + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundtype.rst b/docs/source/telegram.backgroundtype.rst new file mode 100644 index 00000000000..f890ca12daa --- /dev/null +++ b/docs/source/telegram.backgroundtype.rst @@ -0,0 +1,8 @@ +BackgroundType +============== + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundType + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundtypechattheme.rst b/docs/source/telegram.backgroundtypechattheme.rst new file mode 100644 index 00000000000..d192d4aae34 --- /dev/null +++ b/docs/source/telegram.backgroundtypechattheme.rst @@ -0,0 +1,8 @@ +BackgroundTypeChatTheme +======================= + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundTypeChatTheme + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundtypefill.rst b/docs/source/telegram.backgroundtypefill.rst new file mode 100644 index 00000000000..5c0373c4e3d --- /dev/null +++ b/docs/source/telegram.backgroundtypefill.rst @@ -0,0 +1,8 @@ +BackgroundTypeFill +================== + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundTypeFill + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundtypepattern.rst b/docs/source/telegram.backgroundtypepattern.rst new file mode 100644 index 00000000000..763a6e69c8d --- /dev/null +++ b/docs/source/telegram.backgroundtypepattern.rst @@ -0,0 +1,8 @@ +BackgroundTypePattern +===================== + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundTypePattern + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.backgroundtypewallpaper.rst b/docs/source/telegram.backgroundtypewallpaper.rst new file mode 100644 index 00000000000..3852cdab592 --- /dev/null +++ b/docs/source/telegram.backgroundtypewallpaper.rst @@ -0,0 +1,8 @@ +BackgroundTypeWallpaper +======================= + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.BackgroundTypeWallpaper + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/telegram.chatbackground.rst b/docs/source/telegram.chatbackground.rst new file mode 100644 index 00000000000..07e28d78b3e --- /dev/null +++ b/docs/source/telegram.chatbackground.rst @@ -0,0 +1,8 @@ +ChatBackground +============== + +.. versionadded:: NEXT.VERSION + +.. autoclass:: telegram.ChatBackground + :members: + :show-inheritance: \ No newline at end of file diff --git a/telegram/__init__.py b/telegram/__init__.py index 304425c4d61..cfc70f9fc78 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -22,6 +22,15 @@ __all__ = ( "Animation", "Audio", + "BackgroundFill", + "BackgroundFillFreeformGradient", + "BackgroundFillGradient", + "BackgroundFillSolid", + "BackgroundType", + "BackgroundTypeChatTheme", + "BackgroundTypeFill", + "BackgroundTypePattern", + "BackgroundTypeWallpaper", "Birthdate", "Bot", "BotCommand", @@ -46,6 +55,7 @@ "CallbackQuery", "Chat", "ChatAdministratorRights", + "ChatBackground", "ChatBoost", "ChatBoostAdded", "ChatBoostRemoved", @@ -258,6 +268,18 @@ from ._callbackquery import CallbackQuery from ._chat import Chat from ._chatadministratorrights import ChatAdministratorRights +from ._chatbackground import ( + BackgroundFill, + BackgroundFillFreeformGradient, + BackgroundFillGradient, + BackgroundFillSolid, + BackgroundType, + BackgroundTypeChatTheme, + BackgroundTypeFill, + BackgroundTypePattern, + BackgroundTypeWallpaper, + ChatBackground, +) from ._chatboost import ( ChatBoost, ChatBoostAdded, diff --git a/telegram/_chatbackground.py b/telegram/_chatbackground.py new file mode 100644 index 00000000000..4630d4b8477 --- /dev/null +++ b/telegram/_chatbackground.py @@ -0,0 +1,494 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects related to chat backgrounds.""" +from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Type + +from telegram import constants +from telegram._files.document import Document +from telegram._telegramobject import TelegramObject +from telegram._utils import enum +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class BackgroundFill(TelegramObject): + """Base class for Telegram BackgroundFill Objects. It can be one of: + + * :class:`telegram.BackgroundFillSolid` + * :class:`telegram.BackgroundFillGradient` + * :class:`telegram.BackgroundFillFreeformGradient` + + .. versionadded:: NEXT.VERSION + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + Args: + type (:obj:`str`): Type of the background fill. Can be one of: + :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` + or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + + Attributes: + type (:obj:`str`): Type of the background fill. Can be one of: + :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` + or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + + """ + + __slots__ = ("type",) + + SOLID: Final[constants.BackgroundFill] = constants.BackgroundFill.SOLID + """:const:`telegram.constants.BackgroundFill.SOLID`""" + GRADIENT: Final[constants.BackgroundFill] = constants.BackgroundFill.GRADIENT + """:const:`telegram.constants.BackgroundFill.GRADIENT`""" + FREEFORM_GRADIENT: Final[constants.BackgroundFill] = constants.BackgroundFill.FREEFORM_GRADIENT + """:const:`telegram.constants.BackgroundFill.FREEFORM_GRADIENT`""" + + def __init__( + self, + type: str, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required by all subclasses + self.type: str = enum.get_member(constants.BackgroundFill, type, type) + + self._id_attrs = (self.type,) + self._freeze() + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BackgroundFill"]: + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) + + if not data: + return None + + _class_mapping: Dict[str, Type[BackgroundFill]] = { + cls.SOLID: BackgroundFillSolid, + cls.GRADIENT: BackgroundFillGradient, + cls.FREEFORM_GRADIENT: BackgroundFillFreeformGradient, + } + + if cls is BackgroundFill and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data=data, bot=bot) + + return super().de_json(data=data, bot=bot) + + +class BackgroundFillSolid(BackgroundFill): + """ + The background is filled using the selected color. + + .. versionadded:: NEXT.VERSION + + Args: + color (:obj:`int`): The color of the background fill in the `RGB24` format. + + Attributes: + type (:obj:`str`): Type of the background fill. Always + :attr:`~telegram.BackgroundFill.SOLID`. + color (:obj:`int`): The color of the background fill in the `RGB24` format. + """ + + __slots__ = ("color",) + + def __init__( + self, + color: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.SOLID, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.color: int = color + + +class BackgroundFillGradient(BackgroundFill): + """ + The background is a gradient fill. + + .. versionadded:: NEXT.VERSION + + Args: + top_color (:obj:`int`): Top color of the gradient in the `RGB24` format. + bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. + rotation_angle (:obj:`int`): Clockwise rotation angle of the background + fill in degrees; `0-359`. + + Attributes: + type (:obj:`str`): Type of the background fill. Always + :attr:`~telegram.BackgroundFill.GRADIENT`. + top_color (:obj:`int`): Top color of the gradient in the `RGB24` format. + bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. + rotation_angle (:obj:`int`): Clockwise rotation angle of the background + fill in degrees; `0-359`. + """ + + __slots__ = ("bottom_color", "rotation_angle", "top_color") + + def __init__( + self, + top_color: int, + bottom_color: int, + rotation_angle: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.GRADIENT, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.top_color: int = top_color + self.bottom_color: int = bottom_color + self.rotation_angle: int = rotation_angle + + +class BackgroundFillFreeformGradient(BackgroundFill): + """ + The background is a freeform gradient that rotates after every message in the chat. + + .. versionadded:: NEXT.VERSION + + Args: + colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to + generate the freeform gradient in the `RGB24` format + + Attributes: + type (:obj:`str`): Type of the background fill. Always + :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to + generate the freeform gradient in the `RGB24` format + """ + + __slots__ = ("colors",) + + def __init__( + self, + colors: Sequence[int], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.FREEFORM_GRADIENT, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.colors: Sequence[int] = parse_sequence_arg(colors) + + +class BackgroundType(TelegramObject): + """Base class for Telegram BackgroundType Objects. It can be one of: + + * :class:`telegram.BackgroundTypeFill` + * :class:`telegram.BackgroundTypeWallpaper` + * :class:`telegram.BackgroundTypePattern` + * :class:`telegram.BackgroundTypeChatTheme`. + + .. versionadded:: NEXT.VERSION + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + Args: + type (:obj:`str`): Type of the background. Can be one of: + :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` + :attr:`~telegram.BackgroundType.PATTERN` or + :attr:`~telegram.BackgroundType.CHAT_THEME`. + + Attributes: + type (:obj:`str`): Type of the background. Can be one of: + :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` + :attr:`~telegram.BackgroundType.PATTERN` or + :attr:`~telegram.BackgroundType.CHAT_THEME`. + + """ + + __slots__ = ("type",) + + FILL: Final[constants.BackgroundType] = constants.BackgroundType.FILL + """:const:`telegram.constants.BackgroundType.FILL`""" + WALLPAPER: Final[constants.BackgroundType] = constants.BackgroundType.WALLPAPER + """:const:`telegram.constants.BackgroundType.WALLPAPER`""" + PATTERN: Final[constants.BackgroundType] = constants.BackgroundType.PATTERN + """:const:`telegram.constants.BackgroundType.PATTERN`""" + CHAT_THEME: Final[constants.BackgroundType] = constants.BackgroundType.CHAT_THEME + """:const:`telegram.constants.BackgroundType.CHAT_THEME`""" + + def __init__( + self, + type: str, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required by all subclasses + self.type: str = enum.get_member(constants.BackgroundType, type, type) + + self._id_attrs = (self.type,) + self._freeze() + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BackgroundType"]: + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) + + if not data: + return None + + _class_mapping: Dict[str, Type[BackgroundType]] = { + cls.FILL: BackgroundTypeFill, + cls.WALLPAPER: BackgroundTypeWallpaper, + cls.PATTERN: BackgroundTypePattern, + cls.CHAT_THEME: BackgroundTypeChatTheme, + } + + if cls is BackgroundType and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data=data, bot=bot) + + if "fill" in data: + data["fill"] = BackgroundFill.de_json(data.get("fill"), bot) + + if "document" in data: + data["document"] = Document.de_json(data.get("document"), bot) + + return super().de_json(data=data, bot=bot) + + +class BackgroundTypeFill(BackgroundType): + """ + The background is automatically filled based on the selected colors. + + .. versionadded:: NEXT.VERSION + + Args: + fill (:obj:`telegram.BackgroundFill`): The background fill. + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; `0-100`. + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.FILL`. + fill (:obj:`telegram.BackgroundFill`): The background fill. + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; `0-100`. + """ + + __slots__ = ("dark_theme_dimming", "fill") + + def __init__( + self, + fill: BackgroundFill, + dark_theme_dimming: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.FILL, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.fill: BackgroundFill = fill + self.dark_theme_dimming: int = dark_theme_dimming + + +class BackgroundTypeWallpaper(BackgroundType): + """ + The background is a wallpaper in the `JPEG` format. + + .. versionadded:: NEXT.VERSION + + Args: + document (:obj:`telegram.Document`): Document with the wallpaper + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; `0-100` + is_blurred (:obj:`bool`, optional): :obj:`True`, if the wallpaper is downscaled to fit + in a `450x450` square and then box-blurred with radius `12` + is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly + when the device is tilted + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.WALLPAPER`. + document (:obj:`telegram.Document`): Document with the wallpaper + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; `0-100` + is_blurred (:obj:`bool`): Optional. :obj:`True`, if the wallpaper is downscaled to fit + in a `450x450` square and then box-blurred with radius `12` + is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly + when the device is tilted + """ + + __slots__ = ("dark_theme_dimming", "document", "is_blurred", "is_moving") + + def __init__( + self, + document: Document, + dark_theme_dimming: int, + is_blurred: Optional[bool] = None, + is_moving: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.WALLPAPER, api_kwargs=api_kwargs) + + with self._unfrozen(): + # Required + self.document: Document = document + self.dark_theme_dimming: int = dark_theme_dimming + # Optionals + self.is_blurred: Optional[bool] = is_blurred + self.is_moving: Optional[bool] = is_moving + + +class BackgroundTypePattern(BackgroundType): + """ + The background is a `PNG` or `TGV` (gzipped subset of `SVG` with `MIME` type + `"application/x-tgwallpattern"`) pattern to be combined with the background fill + chosen by the user. + + .. versionadded:: NEXT.VERSION + + Args: + document (:obj:`telegram.Document`): Document with the pattern. + fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with + the pattern. + intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled + background; `0-100`. + is_inverted (:obj:`int`, optional): :obj:`True`, if the background fill must be applied + only to the pattern itself. All other pixels are black in this case. For dark + themes only. + is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly + when the device is tilted. + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.PATTERN`. + document (:obj:`telegram.Document`): Document with the pattern. + fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with + the pattern. + intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled + background; `0-100`. + is_inverted (:obj:`int`): Optional. :obj:`True`, if the background fill must be applied + only to the pattern itself. All other pixels are black in this case. For dark + themes only. + is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly + when the device is tilted. + """ + + __slots__ = ( + "document", + "fill", + "intensity", + "is_inverted", + "is_moving", + ) + + def __init__( + self, + document: Document, + fill: BackgroundFill, + intensity: int, + is_inverted: Optional[bool] = None, + is_moving: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.PATTERN, api_kwargs=api_kwargs) + + with self._unfrozen(): + # Required + self.document: Document = document + self.fill: BackgroundFill = fill + self.intensity: int = intensity + # Optionals + self.is_inverted: Optional[bool] = is_inverted + self.is_moving: Optional[bool] = is_moving + + +class BackgroundTypeChatTheme(BackgroundType): + """ + The background is taken directly from a built-in chat theme. + + .. versionadded:: NEXT.VERSION + + Args: + theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji. + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.CHAT_THEME`. + theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji. + """ + + __slots__ = ("theme_name",) + + def __init__( + self, + theme_name: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.CHAT_THEME, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.theme_name: str = theme_name + + +class ChatBackground(TelegramObject): + """ + This object represents a chat background. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + .. versionadded:: NEXT.VERSION + + Args: + type (:obj:`telegram.BackgroundType`): Type of the background. + + Attributes: + type (:obj:`telegram.BackgroundType`): Type of the background. + """ + + __slots__ = ("type",) + + def __init__( + self, + type: BackgroundType, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.type: BackgroundType = type + + self._id_attrs = (self.type,) + self._freeze() + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatBackground"]: + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) + + if not data: + return None + + data["type"] = BackgroundType.de_json(data.get("type"), bot) + + return super().de_json(data=data, bot=bot) diff --git a/telegram/_message.py b/telegram/_message.py index f48418a8f8f..92b0b06f0da 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -25,6 +25,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, TypedDict, Union from telegram._chat import Chat +from telegram._chatbackground import ChatBackground from telegram._chatboost import ChatBoostAdded from telegram._dice import Dice from telegram._files.animation import Animation @@ -553,6 +554,11 @@ class Message(MaybeInaccessibleMessage): .. versionadded:: 21.1 + chat_background_set (:obj:`telegram.ChatBackground`, optional): Service message: chat + background set. + + .. versionadded:: NEXT.VERSION + Attributes: message_id (:obj:`int`): Unique message identifier inside this chat. from_user (:class:`telegram.User`): Optional. Sender of the message; empty for messages @@ -853,6 +859,11 @@ class Message(MaybeInaccessibleMessage): .. versionadded:: 21.1 + chat_background_set (:obj:`telegram.ChatBackground`): Optional. Service message: chat + background set + + .. versionadded:: Next.Version + .. |custom_emoji_no_md1_support| replace:: Since custom emoji entities are not supported by :attr:`~telegram.constants.ParseMode.MARKDOWN`, this method now raises a :exc:`ValueError` when encountering a custom emoji. @@ -876,6 +887,7 @@ class Message(MaybeInaccessibleMessage): "caption", "caption_entities", "channel_chat_created", + "chat_background_set", "chat_shared", "connected_website", "contact", @@ -1029,6 +1041,7 @@ def __init__( business_connection_id: Optional[str] = None, sender_business_bot: Optional[User] = None, is_from_offline: Optional[bool] = None, + chat_background_set: Optional[ChatBackground] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -1127,6 +1140,7 @@ def __init__( self.business_connection_id: Optional[str] = business_connection_id self.sender_business_bot: Optional[User] = sender_business_bot self.is_from_offline: Optional[bool] = is_from_offline + self.chat_background_set: Optional[ChatBackground] = chat_background_set self._effective_attachment = DEFAULT_NONE @@ -1241,6 +1255,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Message"]: ) data["users_shared"] = UsersShared.de_json(data.get("users_shared"), bot) data["chat_shared"] = ChatShared.de_json(data.get("chat_shared"), bot) + data["chat_background_set"] = ChatBackground.de_json(data.get("chat_background_set"), bot) # Unfortunately, this needs to be here due to cyclic imports from telegram._giveaway import ( # pylint: disable=import-outside-toplevel diff --git a/telegram/constants.py b/telegram/constants.py index e7ff6d03823..d3e3ed93c21 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -37,6 +37,8 @@ "SUPPORTED_WEBHOOK_PORTS", "ZERO_DATE", "AccentColor", + "BackgroundFill", + "BackgroundType", "BotCommandLimit", "BotCommandScopeType", "BotDescriptionLimit", @@ -1726,6 +1728,11 @@ class MessageType(StringEnum): .. versionadded:: 20.8 """ + CHAT_BACKGROUND_SET = "chat_background_set" + """:obj:`str`: Messages with :attr:`telegram.Message.chat_background_set`. + + .. versionadded:: NEXT.VERSION + """ CONNECTED_WEBSITE = "connected_website" """:obj:`str`: Messages with :attr:`telegram.Message.connected_website`.""" CONTACT = "contact" @@ -2878,3 +2885,39 @@ class ReactionEmoji(StringEnum): """:obj:`str`: Woman Shrugging""" POUTING_FACE = "😡" """:obj:`str`: Pouting face""" + + +class BackgroundType(StringEnum): + """This enum contains the available types of :class:`telegram.BackgroundType`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: NEXT.VERSION + """ + + __slots__ = () + + FILL = "fill" + """:obj:`str`: A :class:`telegram.BackgroundType` with fill background.""" + WALLPAPER = "wallpaper" + """:obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background.""" + PATTERN = "pattern" + """:obj:`str`: A :class:`telegram.BackgroundType` with pattern background.""" + CHAT_THEME = "chat_theme" + """:obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background.""" + + +class BackgroundFill(StringEnum): + """This enum contains the available types of :class:`telegram.BackgroundFill`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: NEXT.VERSION + """ + + __slots__ = () + + SOLID = "solid" + """:obj:`str`: A :class:`telegram.BackgroundFill` with solid fill.""" + GRADIENT = "gradient" + """:obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill.""" + FREEFORM_GRADIENT = "freeform_gradient" + """:obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill.""" diff --git a/tests/test_chatbackground.py b/tests/test_chatbackground.py new file mode 100644 index 00000000000..f189896bc99 --- /dev/null +++ b/tests/test_chatbackground.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# 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 inspect +from copy import deepcopy +from typing import Union + +import pytest + +from telegram import ( + BackgroundFill, + BackgroundFillFreeformGradient, + BackgroundFillGradient, + BackgroundFillSolid, + BackgroundType, + BackgroundTypeChatTheme, + BackgroundTypeFill, + BackgroundTypePattern, + BackgroundTypeWallpaper, + Dice, + Document, +) +from tests.auxil.slots import mro_slots + +ignored = ["self", "api_kwargs"] + + +class BFDefaults: + color = 0 + top_color = 1 + bottom_color = 2 + rotation_angle = 45 + colors = [0, 1, 2] + + +def background_fill_solid(): + return BackgroundFillSolid(BFDefaults.color) + + +def background_fill_gradient(): + return BackgroundFillGradient( + BFDefaults.top_color, BFDefaults.bottom_color, BFDefaults.rotation_angle + ) + + +def background_fill_freeform_gradient(): + return BackgroundFillFreeformGradient(BFDefaults.colors) + + +class BTDefaults: + document = Document(1, 2) + fill = BackgroundFillSolid(color=0) + dark_theme_dimming = 20 + is_blurred = True + is_moving = False + intensity = 90 + is_inverted = False + theme_name = "ice" + + +def background_type_fill(): + return BackgroundTypeFill(BTDefaults.fill, BTDefaults.dark_theme_dimming) + + +def background_type_wallpaper(): + return BackgroundTypeWallpaper( + BTDefaults.document, + BTDefaults.dark_theme_dimming, + BTDefaults.is_blurred, + BTDefaults.is_moving, + ) + + +def background_type_pattern(): + return BackgroundTypePattern( + BTDefaults.document, + BTDefaults.fill, + BTDefaults.intensity, + BTDefaults.is_inverted, + BTDefaults.is_moving, + ) + + +def background_type_chat_theme(): + return BackgroundTypeChatTheme(BTDefaults.theme_name) + + +def make_json_dict( + instance: Union[BackgroundType, BackgroundFill], include_optional_args: bool = False +) -> dict: + """Used to make the json dict which we use for testing de_json. Similar to iter_args()""" + json_dict = {"type": instance.type} + sig = inspect.signature(instance.__class__.__init__) + + for param in sig.parameters.values(): + if param.name in ignored: # ignore irrelevant params + continue + + val = getattr(instance, param.name) + # Compulsory args- + if param.default is inspect.Parameter.empty: + if hasattr(val, "to_dict"): # convert the user object or any future ones to dict. + val = val.to_dict() + json_dict[param.name] = val + + # If we want to test all args (for de_json)- + elif param.default is not inspect.Parameter.empty and include_optional_args: + json_dict[param.name] = val + return json_dict + + +def iter_args( + instance: Union[BackgroundType, BackgroundFill], + de_json_inst: Union[BackgroundType, BackgroundFill], + include_optional: bool = False, +): + """ + We accept both the regular instance and de_json created instance and iterate over them for + easy one line testing later one. + """ + yield instance.type, de_json_inst.type # yield this here cause it's not available in sig. + + sig = inspect.signature(instance.__class__.__init__) + for param in sig.parameters.values(): + if param.name in ignored: + continue + inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name) + if ( + param.default is not inspect.Parameter.empty and include_optional + ) or param.default is inspect.Parameter.empty: + yield inst_at, json_at + + +@pytest.fixture() +def background_type(request): + return request.param() + + +@pytest.mark.parametrize( + "background_type", + [ + background_type_fill, + background_type_wallpaper, + background_type_pattern, + background_type_chat_theme, + ], + indirect=True, +) +class TestBackgroundTypeWithoutRequest: + def test_slot_behaviour(self, background_type): + inst = background_type + for attr in inst.__slots__: + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" + + def test_de_json_required_args(self, bot, background_type): + cls = background_type.__class__ + assert cls.de_json({}, bot) is None + + json_dict = make_json_dict(background_type) + const_background_type = BackgroundType.de_json(json_dict, bot) + assert const_background_type.api_kwargs == {} + + assert isinstance(const_background_type, BackgroundType) + assert isinstance(const_background_type, cls) + for bg_type_at, const_bg_type_at in iter_args(background_type, const_background_type): + assert bg_type_at == const_bg_type_at + + def test_de_json_all_args(self, bot, background_type): + json_dict = make_json_dict(background_type, include_optional_args=True) + const_background_type = BackgroundType.de_json(json_dict, bot) + + assert const_background_type.api_kwargs == {} + + assert isinstance(const_background_type, BackgroundType) + assert isinstance(const_background_type, background_type.__class__) + for bg_type_at, const_bg_type_at in iter_args( + background_type, const_background_type, True + ): + assert bg_type_at == const_bg_type_at + + def test_de_json_invalid_type(self, background_type, bot): + json_dict = {"type": "invalid", "theme_name": BTDefaults.theme_name} + background_type = BackgroundType.de_json(json_dict, bot) + + assert type(background_type) is BackgroundType + assert background_type.type == "invalid" + + def test_de_json_subclass(self, background_type, bot, chat_id): + """This makes sure that e.g. BackgroundTypeFill(data, bot) never returns a + BackgroundTypeWallpaper instance.""" + cls = background_type.__class__ + json_dict = make_json_dict(background_type, True) + assert type(cls.de_json(json_dict, bot)) is cls + + def test_to_dict(self, background_type): + bg_type_dict = background_type.to_dict() + + assert isinstance(bg_type_dict, dict) + assert bg_type_dict["type"] == background_type.type + + for slot in background_type.__slots__: # additional verification for the optional args + if slot in ("fill", "document"): + assert (getattr(background_type, slot)).to_dict() == bg_type_dict[slot] + continue + assert getattr(background_type, slot) == bg_type_dict[slot] + + def test_equality(self, background_type): + a = BackgroundType(type="type") + b = BackgroundType(type="type") + c = background_type + d = deepcopy(background_type) + e = Dice(4, "emoji") + + assert a == b + assert hash(a) == hash(b) + + assert a != c + assert hash(a) != hash(c) + + assert a != d + assert hash(a) != hash(d) + + assert a != e + assert hash(a) != hash(e) + + assert c == d + assert hash(c) == hash(d) + + assert c != e + assert hash(c) != hash(e) + + +@pytest.fixture() +def background_fill(request): + return request.param() + + +@pytest.mark.parametrize( + "background_fill", + [ + background_fill_solid, + background_fill_gradient, + background_fill_freeform_gradient, + ], + indirect=True, +) +class TestBackgroundFillWithoutRequest: + def test_slot_behaviour(self, background_fill): + inst = background_fill + for attr in inst.__slots__: + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" + + def test_de_json_required_args(self, bot, background_fill): + cls = background_fill.__class__ + assert cls.de_json({}, bot) is None + + json_dict = make_json_dict(background_fill) + const_background_fill = BackgroundFill.de_json(json_dict, bot) + assert const_background_fill.api_kwargs == {} + + assert isinstance(const_background_fill, BackgroundFill) + assert isinstance(const_background_fill, cls) + for bg_fill_at, const_bg_fill_at in iter_args(background_fill, const_background_fill): + assert bg_fill_at == const_bg_fill_at + + def test_de_json_all_args(self, bot, background_fill): + json_dict = make_json_dict(background_fill, include_optional_args=True) + const_background_fill = BackgroundFill.de_json(json_dict, bot) + + assert const_background_fill.api_kwargs == {} + + assert isinstance(const_background_fill, BackgroundFill) + assert isinstance(const_background_fill, background_fill.__class__) + for bg_fill_at, const_bg_fill_at in iter_args( + background_fill, const_background_fill, True + ): + assert bg_fill_at == const_bg_fill_at + + def test_de_json_invalid_type(self, background_fill, bot): + json_dict = {"type": "invalid", "theme_name": BTDefaults.theme_name} + background_fill = BackgroundFill.de_json(json_dict, bot) + + assert type(background_fill) is BackgroundFill + assert background_fill.type == "invalid" + + def test_de_json_subclass(self, background_fill, bot): + """This makes sure that e.g. BackgroundFillSolid(data, bot) never returns a + BackgroundFillGradient instance.""" + cls = background_fill.__class__ + json_dict = make_json_dict(background_fill, True) + assert type(cls.de_json(json_dict, bot)) is cls + + def test_to_dict(self, background_fill): + bg_fill_dict = background_fill.to_dict() + + assert isinstance(bg_fill_dict, dict) + assert bg_fill_dict["type"] == background_fill.type + + for slot in background_fill.__slots__: # additional verification for the optional args + if slot == "colors": + assert getattr(background_fill, slot) == tuple(bg_fill_dict[slot]) + continue + assert getattr(background_fill, slot) == bg_fill_dict[slot] + + def test_equality(self, background_fill): + a = BackgroundFill(type="type") + b = BackgroundFill(type="type") + c = background_fill + d = deepcopy(background_fill) + e = Dice(4, "emoji") + + assert a == b + assert hash(a) == hash(b) + + assert a != c + assert hash(a) != hash(c) + + assert a != d + assert hash(a) != hash(d) + + assert a != e + assert hash(a) != hash(e) + + assert c == d + assert hash(c) == hash(d) + + assert c != e + assert hash(c) != hash(e) diff --git a/tests/test_message.py b/tests/test_message.py index 440ec50651c..46a7f89b865 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -24,8 +24,10 @@ from telegram import ( Animation, Audio, + BackgroundTypeChatTheme, Bot, Chat, + ChatBackground, ChatBoostAdded, ChatShared, Contact, @@ -270,6 +272,7 @@ def message(bot): {"is_from_offline": True}, {"sender_business_bot": User(1, "BusinessBot", True)}, {"business_connection_id": "123456789"}, + {"chat_background_set": ChatBackground(type=BackgroundTypeChatTheme("ice"))}, ], ids=[ "reply", @@ -338,6 +341,7 @@ def message(bot): "sender_business_bot", "business_connection_id", "is_from_offline", + "chat_background_set", ], ) def message_params(bot, request): From f512c0f9b3866cd0e67889612835e18ad0777379 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Fri, 10 May 2024 03:46:55 +0300 Subject: [PATCH 04/12] Fix test_enum_types (exclude `Chatbackground.type`) --- tests/test_enum_types.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_enum_types.py b/tests/test_enum_types.py index 9e7140ee1df..b16002c6642 100644 --- a/tests/test_enum_types.py +++ b/tests/test_enum_types.py @@ -27,7 +27,10 @@ / "_passport", } -exclude_patterns = {re.compile(re.escape("self.type: ReactionType = type"))} +exclude_patterns = { + re.compile(re.escape("self.type: ReactionType = type")), + re.compile(re.escape("self.type: BackgroundType = type")), +} def test_types_are_converted_to_enum(): From fbac296843ef0b0cb2ae233d0d76c89a67e7a60d Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Fri, 10 May 2024 06:17:32 +0300 Subject: [PATCH 05/12] Add tg-const values and improve docstrings formatting --- docs/source/telegram.backgroundfill.rst | 2 +- telegram/_bot.py | 3 +- telegram/_chatbackground.py | 96 ++++++++++++++----------- telegram/constants.py | 74 +++++++++++++++++++ 4 files changed, 133 insertions(+), 42 deletions(-) diff --git a/docs/source/telegram.backgroundfill.rst b/docs/source/telegram.backgroundfill.rst index 5f7e68f9f08..df9310e7ab2 100644 --- a/docs/source/telegram.backgroundfill.rst +++ b/docs/source/telegram.backgroundfill.rst @@ -1,5 +1,5 @@ BackgroundFill -======================= +============== .. versionadded:: NEXT.VERSION diff --git a/telegram/_bot.py b/telegram/_bot.py index 937d186ff01..2708fd2e439 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -2760,7 +2760,8 @@ async def edit_message_live_location( reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for a new inline keyboard. live_period (:obj:`int`, optional): New period in seconds during which the location - can be updated, starting from the message send date. If `0x7FFFFFFF` is specified, + can be updated, starting from the message send date. If + :tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` is specified, then the location can be updated forever. Otherwise, the new value must not exceed the current live_period by more than a day, and the live location expiration date must remain within the next 90 days. If not specified, then `live_period` diff --git a/telegram/_chatbackground.py b/telegram/_chatbackground.py index 4630d4b8477..deab8b2cfa7 100644 --- a/telegram/_chatbackground.py +++ b/telegram/_chatbackground.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains objects related to chat backgrounds.""" -from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Type +from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type from telegram import constants from telegram._files.document import Document @@ -44,14 +44,13 @@ class BackgroundFill(TelegramObject): Args: type (:obj:`str`): Type of the background fill. Can be one of: - :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` - or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` + or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. Attributes: type (:obj:`str`): Type of the background fill. Can be one of: - :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` - or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. - + :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` + or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. """ __slots__ = ("type",) @@ -135,7 +134,10 @@ class BackgroundFillGradient(BackgroundFill): top_color (:obj:`int`): Top color of the gradient in the `RGB24` format. bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. rotation_angle (:obj:`int`): Clockwise rotation angle of the background - fill in degrees; `0-359`. + fill in degrees; + :tg-const:`telegram.constants.BackgroundFillLimit.MIN_ROTATION_ANGLE`- + :tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. + Attributes: type (:obj:`str`): Type of the background fill. Always @@ -143,7 +145,9 @@ class BackgroundFillGradient(BackgroundFill): top_color (:obj:`int`): Top color of the gradient in the `RGB24` format. bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. rotation_angle (:obj:`int`): Clockwise rotation angle of the background - fill in degrees; `0-359`. + fill in degrees; + :tg-const:`telegram.constants.BackgroundFillLimit.MIN_ROTATION_ANGLE`- + :tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. """ __slots__ = ("bottom_color", "rotation_angle", "top_color") @@ -172,13 +176,13 @@ class BackgroundFillFreeformGradient(BackgroundFill): Args: colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to - generate the freeform gradient in the `RGB24` format + generate the freeform gradient in the `RGB24` format Attributes: type (:obj:`str`): Type of the background fill. Always - :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to - generate the freeform gradient in the `RGB24` format + generate the freeform gradient in the `RGB24` format """ __slots__ = ("colors",) @@ -192,7 +196,7 @@ def __init__( super().__init__(type=self.FREEFORM_GRADIENT, api_kwargs=api_kwargs) with self._unfrozen(): - self.colors: Sequence[int] = parse_sequence_arg(colors) + self.colors: Tuple[int] = parse_sequence_arg(colors) class BackgroundType(TelegramObject): @@ -210,15 +214,15 @@ class BackgroundType(TelegramObject): Args: type (:obj:`str`): Type of the background. Can be one of: - :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` - :attr:`~telegram.BackgroundType.PATTERN` or - :attr:`~telegram.BackgroundType.CHAT_THEME`. + :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` + :attr:`~telegram.BackgroundType.PATTERN` or + :attr:`~telegram.BackgroundType.CHAT_THEME`. Attributes: type (:obj:`str`): Type of the background. Can be one of: - :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` - :attr:`~telegram.BackgroundType.PATTERN` or - :attr:`~telegram.BackgroundType.CHAT_THEME`. + :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` + :attr:`~telegram.BackgroundType.PATTERN` or + :attr:`~telegram.BackgroundType.CHAT_THEME`. """ @@ -282,14 +286,18 @@ class BackgroundTypeFill(BackgroundType): Args: fill (:obj:`telegram.BackgroundFill`): The background fill. dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a - percentage; `0-100`. + percentage; + :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- + :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. Attributes: type (:obj:`str`): Type of the background. Always - :attr:`~telegram.BackgroundType.FILL`. + :attr:`~telegram.BackgroundType.FILL`. fill (:obj:`telegram.BackgroundFill`): The background fill. dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a - percentage; `0-100`. + percentage; + :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- + :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. """ __slots__ = ("dark_theme_dimming", "fill") @@ -317,22 +325,26 @@ class BackgroundTypeWallpaper(BackgroundType): Args: document (:obj:`telegram.Document`): Document with the wallpaper dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a - percentage; `0-100` + percentage; + :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- + :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. is_blurred (:obj:`bool`, optional): :obj:`True`, if the wallpaper is downscaled to fit - in a `450x450` square and then box-blurred with radius `12` + in a 450x450 square and then box-blurred with radius 12 is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly - when the device is tilted + when the device is tilted Attributes: type (:obj:`str`): Type of the background. Always - :attr:`~telegram.BackgroundType.WALLPAPER`. + :attr:`~telegram.BackgroundType.WALLPAPER`. document (:obj:`telegram.Document`): Document with the wallpaper dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a - percentage; `0-100` + percentage; + :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- + :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. is_blurred (:obj:`bool`): Optional. :obj:`True`, if the wallpaper is downscaled to fit - in a `450x450` square and then box-blurred with radius `12` + in a 450x450 square and then box-blurred with radius 12 is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly - when the device is tilted + when the device is tilted """ __slots__ = ("dark_theme_dimming", "document", "is_blurred", "is_moving") @@ -368,28 +380,32 @@ class BackgroundTypePattern(BackgroundType): Args: document (:obj:`telegram.Document`): Document with the pattern. fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with - the pattern. + the pattern. intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled - background; `0-100`. + background; + :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_INTENSITY`- + :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. is_inverted (:obj:`int`, optional): :obj:`True`, if the background fill must be applied - only to the pattern itself. All other pixels are black in this case. For dark - themes only. + only to the pattern itself. All other pixels are black in this case. For dark + themes only. is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly - when the device is tilted. + when the device is tilted. Attributes: type (:obj:`str`): Type of the background. Always - :attr:`~telegram.BackgroundType.PATTERN`. + :attr:`~telegram.BackgroundType.PATTERN`. document (:obj:`telegram.Document`): Document with the pattern. fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with - the pattern. + the pattern. intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled - background; `0-100`. + background; + :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_INTENSITY`- + :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. is_inverted (:obj:`int`): Optional. :obj:`True`, if the background fill must be applied - only to the pattern itself. All other pixels are black in this case. For dark - themes only. + only to the pattern itself. All other pixels are black in this case. For dark + themes only. is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly - when the device is tilted. + when the device is tilted. """ __slots__ = ( @@ -433,7 +449,7 @@ class BackgroundTypeChatTheme(BackgroundType): Attributes: type (:obj:`str`): Type of the background. Always - :attr:`~telegram.BackgroundType.CHAT_THEME`. + :attr:`~telegram.BackgroundType.CHAT_THEME`. theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji. """ diff --git a/telegram/constants.py b/telegram/constants.py index d3e3ed93c21..37b9b65e896 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -38,7 +38,9 @@ "ZERO_DATE", "AccentColor", "BackgroundFill", + "BackgroundFillLimit", "BackgroundType", + "BackgroundTypeLimit", "BotCommandLimit", "BotCommandScopeType", "BotDescriptionLimit", @@ -824,6 +826,63 @@ class ChatLimit(IntEnum): """ +class BackgroundTypeLimit(IntEnum): + """This enum contains limitations for :class:`telegram.BackgroundTypeFill`, + :class:`telegram.BackgroundTypeWallpaper` and :class:`telegram.BackgroundTypePattern`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: NEXT.VERSION + """ + + __slots__ = () + + MIN_DIMMING = 0 + """:obj:`int`: Minimum value allowed for: + + * :paramref:`~telegram.BackgroundTypeFill.dark_theme_dimming` parameter of + :class:`telegram.BackgroundTypeFill` + * :paramref:`~telegram.BackgroundTypeWallpaper.dark_theme_dimming` parameter of + :class:`telegram.BackgroundTypeWallpaper` + """ + MAX_DIMMING = 100 + """:obj:`int`: Maximum value allowed for: + + * :paramref:`~telegram.BackgroundTypeFill.dark_theme_dimming` parameter of + :class:`telegram.BackgroundTypeFill` + * :paramref:`~telegram.BackgroundTypeWallpaper.dark_theme_dimming` parameter of + :class:`telegram.BackgroundTypeWallpaper` + """ + MIN_INTENSITY = 0 + """:obj:`int`: Minimum value allowed for :paramref:`~telegram.BackgroundTypePattern.intensity` + parameter of :class:`telegram.BackgroundTypePattern` + """ + MAX_INTENSITY = 100 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.BackgroundTypePattern.intensity` + parameter of :class:`telegram.BackgroundTypePattern` + """ + + +class BackgroundFillLimit(IntEnum): + """This enum contains limitations for :class:`telegram.BackgroundFillGradient`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: NEXT.VERSION + """ + + __slots__ = () + + MIN_ROTATION_ANGLE = 0 + """:obj:`int`: Minimum value allowed for + :paramref:`~telegram.BackgroundFillGradient.rotation_angle` parameter of + :class:`telegram.BackgroundFillGradient` + """ + MAX_ROTATION_ANGLE = 359 + """:obj:`int`: Maximum value allowed for: + :paramref:`~telegram.BackgroundFillGradient.rotation_angle` parameter of + :class:`telegram.BackgroundFillGradient` + """ + + class ChatMemberStatus(StringEnum): """This enum contains the available states for :class:`telegram.ChatMember`. The enum members of this enumeration are instances of :class:`str` and can be treated as such. @@ -1429,6 +1488,21 @@ class LocationLimit(IntEnum): :meth:`telegram.Bot.send_location` """ + LIVE_PERIOD_FOREVER = int(hex(0x7FFFFFFF), 16) + """:obj:`int`: Value for live locations that can be edited indefinitely. Passed in: + + * :paramref:`~telegram.InlineQueryResultLocation.live_period` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.live_period` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.live_period` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.live_period` parameter of + :meth:`telegram.Bot.send_location` + + .. versionadded:: NEXT.VERSION + """ + MIN_PROXIMITY_ALERT_RADIUS = 1 """:obj:`int`: Minimum value allowed for: From 411386338ef9504f949069dc5fe900b1cdea3e96 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Fri, 10 May 2024 06:24:16 +0300 Subject: [PATCH 06/12] Rename enums of classes `Background{Type, Fill}`. --- telegram/_chatbackground.py | 34 ++++++++++++++++++---------------- telegram/constants.py | 8 ++++---- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/telegram/_chatbackground.py b/telegram/_chatbackground.py index deab8b2cfa7..91bf58ae9db 100644 --- a/telegram/_chatbackground.py +++ b/telegram/_chatbackground.py @@ -55,12 +55,14 @@ class BackgroundFill(TelegramObject): __slots__ = ("type",) - SOLID: Final[constants.BackgroundFill] = constants.BackgroundFill.SOLID - """:const:`telegram.constants.BackgroundFill.SOLID`""" - GRADIENT: Final[constants.BackgroundFill] = constants.BackgroundFill.GRADIENT - """:const:`telegram.constants.BackgroundFill.GRADIENT`""" - FREEFORM_GRADIENT: Final[constants.BackgroundFill] = constants.BackgroundFill.FREEFORM_GRADIENT - """:const:`telegram.constants.BackgroundFill.FREEFORM_GRADIENT`""" + SOLID: Final[constants.BackgroundFillType] = constants.BackgroundFillType.SOLID + """:const:`telegram.constants.BackgroundFillType.SOLID`""" + GRADIENT: Final[constants.BackgroundFillType] = constants.BackgroundFillType.GRADIENT + """:const:`telegram.constants.BackgroundFillType.GRADIENT`""" + FREEFORM_GRADIENT: Final[constants.BackgroundFillType] = ( + constants.BackgroundFillType.FREEFORM_GRADIENT + ) + """:const:`telegram.constants.BackgroundFillType.FREEFORM_GRADIENT`""" def __init__( self, @@ -70,7 +72,7 @@ def __init__( ): super().__init__(api_kwargs=api_kwargs) # Required by all subclasses - self.type: str = enum.get_member(constants.BackgroundFill, type, type) + self.type: str = enum.get_member(constants.BackgroundFillType, type, type) self._id_attrs = (self.type,) self._freeze() @@ -228,14 +230,14 @@ class BackgroundType(TelegramObject): __slots__ = ("type",) - FILL: Final[constants.BackgroundType] = constants.BackgroundType.FILL - """:const:`telegram.constants.BackgroundType.FILL`""" - WALLPAPER: Final[constants.BackgroundType] = constants.BackgroundType.WALLPAPER - """:const:`telegram.constants.BackgroundType.WALLPAPER`""" - PATTERN: Final[constants.BackgroundType] = constants.BackgroundType.PATTERN - """:const:`telegram.constants.BackgroundType.PATTERN`""" - CHAT_THEME: Final[constants.BackgroundType] = constants.BackgroundType.CHAT_THEME - """:const:`telegram.constants.BackgroundType.CHAT_THEME`""" + FILL: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.FILL + """:const:`telegram.constants.BackgroundTypeType.FILL`""" + WALLPAPER: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.WALLPAPER + """:const:`telegram.constants.BackgroundTypeType.WALLPAPER`""" + PATTERN: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.PATTERN + """:const:`telegram.constants.BackgroundTypeType.PATTERN`""" + CHAT_THEME: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.CHAT_THEME + """:const:`telegram.constants.BackgroundTypeType.CHAT_THEME`""" def __init__( self, @@ -245,7 +247,7 @@ def __init__( ): super().__init__(api_kwargs=api_kwargs) # Required by all subclasses - self.type: str = enum.get_member(constants.BackgroundType, type, type) + self.type: str = enum.get_member(constants.BackgroundTypeType, type, type) self._id_attrs = (self.type,) self._freeze() diff --git a/telegram/constants.py b/telegram/constants.py index 37b9b65e896..19cde7002ec 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -37,9 +37,9 @@ "SUPPORTED_WEBHOOK_PORTS", "ZERO_DATE", "AccentColor", - "BackgroundFill", + "BackgroundFillType", "BackgroundFillLimit", - "BackgroundType", + "BackgroundTypeType", "BackgroundTypeLimit", "BotCommandLimit", "BotCommandScopeType", @@ -2961,7 +2961,7 @@ class ReactionEmoji(StringEnum): """:obj:`str`: Pouting face""" -class BackgroundType(StringEnum): +class BackgroundTypeType(StringEnum): """This enum contains the available types of :class:`telegram.BackgroundType`. The enum members of this enumeration are instances of :class:`str` and can be treated as such. @@ -2980,7 +2980,7 @@ class BackgroundType(StringEnum): """:obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background.""" -class BackgroundFill(StringEnum): +class BackgroundFillType(StringEnum): """This enum contains the available types of :class:`telegram.BackgroundFill`. The enum members of this enumeration are instances of :class:`str` and can be treated as such. From 750f02df1cc81ae280f9001efe81634f0e9ab505 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Fri, 10 May 2024 07:19:42 +0300 Subject: [PATCH 07/12] Extend _id_attrs to `Background*` subclasses as well. --- telegram/_chatbackground.py | 36 ++++++++++++++++++++++++++++++++++++ tests/test_chatbackground.py | 16 ++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/telegram/_chatbackground.py b/telegram/_chatbackground.py index 91bf58ae9db..b1d3c74608d 100644 --- a/telegram/_chatbackground.py +++ b/telegram/_chatbackground.py @@ -103,6 +103,9 @@ class BackgroundFillSolid(BackgroundFill): .. versionadded:: NEXT.VERSION + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`color` is equal. + Args: color (:obj:`int`): The color of the background fill in the `RGB24` format. @@ -125,6 +128,8 @@ def __init__( with self._unfrozen(): self.color: int = color + self._id_attrs = (self.color,) + class BackgroundFillGradient(BackgroundFill): """ @@ -132,6 +137,10 @@ class BackgroundFillGradient(BackgroundFill): .. versionadded:: NEXT.VERSION + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`top_color`, :attr:`bottom_color` + and :attr:`rotation_angle` are equal. + Args: top_color (:obj:`int`): Top color of the gradient in the `RGB24` format. bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. @@ -169,6 +178,8 @@ def __init__( self.bottom_color: int = bottom_color self.rotation_angle: int = rotation_angle + self._id_attrs = (self.top_color, self.bottom_color, self.rotation_angle) + class BackgroundFillFreeformGradient(BackgroundFill): """ @@ -176,6 +187,9 @@ class BackgroundFillFreeformGradient(BackgroundFill): .. versionadded:: NEXT.VERSION + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`colors` is equal. + Args: colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to generate the freeform gradient in the `RGB24` format @@ -200,6 +214,8 @@ def __init__( with self._unfrozen(): self.colors: Tuple[int] = parse_sequence_arg(colors) + self._id_attrs = (self.colors,) + class BackgroundType(TelegramObject): """Base class for Telegram BackgroundType Objects. It can be one of: @@ -285,6 +301,9 @@ class BackgroundTypeFill(BackgroundType): .. versionadded:: NEXT.VERSION + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`fill` and :attr:`dark_theme_dimming` are equal. + Args: fill (:obj:`telegram.BackgroundFill`): The background fill. dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a @@ -317,6 +336,8 @@ def __init__( self.fill: BackgroundFill = fill self.dark_theme_dimming: int = dark_theme_dimming + self._id_attrs = (self.fill, self.dark_theme_dimming) + class BackgroundTypeWallpaper(BackgroundType): """ @@ -324,6 +345,9 @@ class BackgroundTypeWallpaper(BackgroundType): .. versionadded:: NEXT.VERSION + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`document` and :attr:`dark_theme_dimming` are equal. + Args: document (:obj:`telegram.Document`): Document with the wallpaper dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a @@ -370,6 +394,8 @@ def __init__( self.is_blurred: Optional[bool] = is_blurred self.is_moving: Optional[bool] = is_moving + self._id_attrs = (self.document, self.dark_theme_dimming) + class BackgroundTypePattern(BackgroundType): """ @@ -379,6 +405,9 @@ class BackgroundTypePattern(BackgroundType): .. versionadded:: NEXT.VERSION + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`document` and :attr:`fill` and :attr:`intensity` are equal. + Args: document (:obj:`telegram.Document`): Document with the pattern. fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with @@ -439,6 +468,8 @@ def __init__( self.is_inverted: Optional[bool] = is_inverted self.is_moving: Optional[bool] = is_moving + self._id_attrs = (self.document, self.fill, self.intensity) + class BackgroundTypeChatTheme(BackgroundType): """ @@ -446,6 +477,9 @@ class BackgroundTypeChatTheme(BackgroundType): .. versionadded:: NEXT.VERSION + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`theme_name` is equal. + Args: theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji. @@ -468,6 +502,8 @@ def __init__( with self._unfrozen(): self.theme_name: str = theme_name + self._id_attrs = (self.theme_name,) + class ChatBackground(TelegramObject): """ diff --git a/tests/test_chatbackground.py b/tests/test_chatbackground.py index f189896bc99..1f8be1eb451 100644 --- a/tests/test_chatbackground.py +++ b/tests/test_chatbackground.py @@ -226,6 +226,11 @@ def test_equality(self, background_type): c = background_type d = deepcopy(background_type) e = Dice(4, "emoji") + sig = inspect.signature(background_type.__class__.__init__) + params = [ + "random" for param in sig.parameters.values() if param.name not in [*ignored, "type"] + ] + f = background_type.__class__(*params) assert a == b assert hash(a) == hash(b) @@ -245,6 +250,9 @@ def test_equality(self, background_type): assert c != e assert hash(c) != hash(e) + assert f != c + assert hash(f) != hash(c) + @pytest.fixture() def background_fill(request): @@ -325,6 +333,11 @@ def test_equality(self, background_fill): c = background_fill d = deepcopy(background_fill) e = Dice(4, "emoji") + sig = inspect.signature(background_fill.__class__.__init__) + params = [ + "random" for param in sig.parameters.values() if param.name not in [*ignored, "type"] + ] + f = background_fill.__class__(*params) assert a == b assert hash(a) == hash(b) @@ -343,3 +356,6 @@ def test_equality(self, background_fill): assert c != e assert hash(c) != hash(e) + + assert f != c + assert hash(f) != hash(c) From f82112bd810252e9ca327230debeda7cee2f5664 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Fri, 10 May 2024 07:51:06 +0300 Subject: [PATCH 08/12] Add `StatusUpdate.CHAT_BACKGROUND_SET` filter & tetsts. --- telegram/ext/filters.py | 12 +++++++++++- tests/ext/test_filters.py | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index f2820d2b25a..72145edb378 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -1909,7 +1909,8 @@ class _All(UpdateFilter): def filter(self, update: Update) -> bool: return bool( # keep this alphabetically sorted for easier maintenance - StatusUpdate.CHAT_CREATED.check_update(update) + StatusUpdate.CHAT_BACKGROUND_SET.check_update(update) + or StatusUpdate.CHAT_CREATED.check_update(update) or StatusUpdate.CHAT_SHARED.check_update(update) or StatusUpdate.CONNECTED_WEBSITE.check_update(update) or StatusUpdate.DELETE_CHAT_PHOTO.check_update(update) @@ -1942,6 +1943,15 @@ def filter(self, update: Update) -> bool: ALL = _All(name="filters.StatusUpdate.ALL") """Messages that contain any of the below.""" + class _ChatBackgroundSet(MessageFilter): + __slots__ = () + + def filter(self, message: Message) -> bool: + return bool(message.chat_background_set) + + CHAT_BACKGROUND_SET = _ChatBackgroundSet(name="filters.StatusUpdate.CHAT_BACKGROUND_SET") + """Messages that contain :attr:`telegram.Message.chat_background_set`.""" + class _ChatCreated(MessageFilter): __slots__ = () diff --git a/tests/ext/test_filters.py b/tests/ext/test_filters.py index 694ea009a6f..fc88e428404 100644 --- a/tests/ext/test_filters.py +++ b/tests/ext/test_filters.py @@ -1090,6 +1090,11 @@ def test_filters_status_update(self, update): assert filters.StatusUpdate.GIVEAWAY_COMPLETED.check_update(update) update.message.giveaway_completed = None + update.message.chat_background_set = "test_background" + assert filters.StatusUpdate.ALL.check_update(update) + assert filters.StatusUpdate.CHAT_BACKGROUND_SET.check_update(update) + update.message.chat_background_set = None + def test_filters_forwarded(self, update, message_origin_user): assert filters.FORWARDED.check_update(update) update.message.forward_origin = MessageOriginHiddenUser(datetime.datetime.utcnow(), 1) From 78d3ec734fe07dbf8fdecd55e530872417f554f4 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Sat, 11 May 2024 03:18:10 +0300 Subject: [PATCH 09/12] Remove zero-valued `tg-const`s. --- telegram/_chatbackground.py | 24 ++++++++---------------- telegram/constants.py | 17 ----------------- 2 files changed, 8 insertions(+), 33 deletions(-) diff --git a/telegram/_chatbackground.py b/telegram/_chatbackground.py index b1d3c74608d..779e2875d8d 100644 --- a/telegram/_chatbackground.py +++ b/telegram/_chatbackground.py @@ -146,8 +146,7 @@ class BackgroundFillGradient(BackgroundFill): bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. rotation_angle (:obj:`int`): Clockwise rotation angle of the background fill in degrees; - :tg-const:`telegram.constants.BackgroundFillLimit.MIN_ROTATION_ANGLE`- - :tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. + 0-:tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. Attributes: @@ -157,8 +156,7 @@ class BackgroundFillGradient(BackgroundFill): bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. rotation_angle (:obj:`int`): Clockwise rotation angle of the background fill in degrees; - :tg-const:`telegram.constants.BackgroundFillLimit.MIN_ROTATION_ANGLE`- - :tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. + 0-:tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. """ __slots__ = ("bottom_color", "rotation_angle", "top_color") @@ -308,8 +306,7 @@ class BackgroundTypeFill(BackgroundType): fill (:obj:`telegram.BackgroundFill`): The background fill. dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a percentage; - :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- - :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. Attributes: type (:obj:`str`): Type of the background. Always @@ -317,8 +314,7 @@ class BackgroundTypeFill(BackgroundType): fill (:obj:`telegram.BackgroundFill`): The background fill. dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a percentage; - :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- - :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. """ __slots__ = ("dark_theme_dimming", "fill") @@ -352,8 +348,7 @@ class BackgroundTypeWallpaper(BackgroundType): document (:obj:`telegram.Document`): Document with the wallpaper dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a percentage; - :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- - :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. is_blurred (:obj:`bool`, optional): :obj:`True`, if the wallpaper is downscaled to fit in a 450x450 square and then box-blurred with radius 12 is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly @@ -365,8 +360,7 @@ class BackgroundTypeWallpaper(BackgroundType): document (:obj:`telegram.Document`): Document with the wallpaper dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a percentage; - :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_DIMMING`- - :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. is_blurred (:obj:`bool`): Optional. :obj:`True`, if the wallpaper is downscaled to fit in a 450x450 square and then box-blurred with radius 12 is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly @@ -414,8 +408,7 @@ class BackgroundTypePattern(BackgroundType): the pattern. intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled background; - :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_INTENSITY`- - :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. is_inverted (:obj:`int`, optional): :obj:`True`, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only. @@ -430,8 +423,7 @@ class BackgroundTypePattern(BackgroundType): the pattern. intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled background; - :tg-const:`telegram.constants.BackgroundTypeLimit.MIN_INTENSITY`- - :tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. is_inverted (:obj:`int`): Optional. :obj:`True`, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only. diff --git a/telegram/constants.py b/telegram/constants.py index 19cde7002ec..26ea10fe15f 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -836,14 +836,6 @@ class BackgroundTypeLimit(IntEnum): __slots__ = () - MIN_DIMMING = 0 - """:obj:`int`: Minimum value allowed for: - - * :paramref:`~telegram.BackgroundTypeFill.dark_theme_dimming` parameter of - :class:`telegram.BackgroundTypeFill` - * :paramref:`~telegram.BackgroundTypeWallpaper.dark_theme_dimming` parameter of - :class:`telegram.BackgroundTypeWallpaper` - """ MAX_DIMMING = 100 """:obj:`int`: Maximum value allowed for: @@ -852,10 +844,6 @@ class BackgroundTypeLimit(IntEnum): * :paramref:`~telegram.BackgroundTypeWallpaper.dark_theme_dimming` parameter of :class:`telegram.BackgroundTypeWallpaper` """ - MIN_INTENSITY = 0 - """:obj:`int`: Minimum value allowed for :paramref:`~telegram.BackgroundTypePattern.intensity` - parameter of :class:`telegram.BackgroundTypePattern` - """ MAX_INTENSITY = 100 """:obj:`int`: Maximum value allowed for :paramref:`~telegram.BackgroundTypePattern.intensity` parameter of :class:`telegram.BackgroundTypePattern` @@ -871,11 +859,6 @@ class BackgroundFillLimit(IntEnum): __slots__ = () - MIN_ROTATION_ANGLE = 0 - """:obj:`int`: Minimum value allowed for - :paramref:`~telegram.BackgroundFillGradient.rotation_angle` parameter of - :class:`telegram.BackgroundFillGradient` - """ MAX_ROTATION_ANGLE = 359 """:obj:`int`: Maximum value allowed for: :paramref:`~telegram.BackgroundFillGradient.rotation_angle` parameter of From bad6ba14f655412625e1dc8ca16e4f27561c4d4a Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Sat, 11 May 2024 03:30:41 +0300 Subject: [PATCH 10/12] Fix pre-commit issues. * sort constants.__all__ * properly annotate `self.colors = parse_sequence_arg(..)` --- telegram/_chatbackground.py | 2 +- telegram/constants.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/telegram/_chatbackground.py b/telegram/_chatbackground.py index 779e2875d8d..2724613af75 100644 --- a/telegram/_chatbackground.py +++ b/telegram/_chatbackground.py @@ -210,7 +210,7 @@ def __init__( super().__init__(type=self.FREEFORM_GRADIENT, api_kwargs=api_kwargs) with self._unfrozen(): - self.colors: Tuple[int] = parse_sequence_arg(colors) + self.colors: Tuple[int, ...] = parse_sequence_arg(colors) self._id_attrs = (self.colors,) diff --git a/telegram/constants.py b/telegram/constants.py index 26ea10fe15f..a12bb08dee3 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -37,10 +37,10 @@ "SUPPORTED_WEBHOOK_PORTS", "ZERO_DATE", "AccentColor", - "BackgroundFillType", "BackgroundFillLimit", - "BackgroundTypeType", + "BackgroundFillType", "BackgroundTypeLimit", + "BackgroundTypeType", "BotCommandLimit", "BotCommandScopeType", "BotDescriptionLimit", From cb9e17e791ca2714b904c48c3bec629c551c1e93 Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Sat, 11 May 2024 03:58:47 +0300 Subject: [PATCH 11/12] Adapt test_offical adding attrs to PTB_IGNORED_PARAMS. --- tests/test_official/exceptions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_official/exceptions.py b/tests/test_official/exceptions.py index 89892741bd4..d812c7f56ed 100644 --- a/tests/test_official/exceptions.py +++ b/tests/test_official/exceptions.py @@ -143,6 +143,8 @@ def ptb_extra_params(object_name: str) -> set[str]: r"MessageOrigin\w+": {"type"}, r"ChatBoostSource\w+": {"source"}, r"ReactionType\w+": {"type"}, + r"BackgroundType\w+": {"type"}, + r"BackgroundFill\w+": {"type"}, } From 7956323adeee9390ad6a04ebf62d6175515f6bdd Mon Sep 17 00:00:00 2001 From: aelkheir <90580077+aelkheir@users.noreply.github.com> Date: Sat, 11 May 2024 04:12:45 +0300 Subject: [PATCH 12/12] Add same attrs to PTB_EXTRA_PARAMS. please pass V --- tests/test_official/exceptions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_official/exceptions.py b/tests/test_official/exceptions.py index d812c7f56ed..128b30089fb 100644 --- a/tests/test_official/exceptions.py +++ b/tests/test_official/exceptions.py @@ -120,6 +120,8 @@ class ParamTypeCheckingExceptions: "ChatBoostSource": {"source"}, # attributes common to all subclasses "MessageOrigin": {"type", "date"}, # attributes common to all subclasses "ReactionType": {"type"}, # attributes common to all subclasses + "BackgroundType": {"type"}, # attributes common to all subclasses + "BackgroundFill": {"type"}, # attributes common to all subclasses "InputTextMessageContent": {"disable_web_page_preview"}, # convenience arg, here for bw compat } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy