From a852b9559612e3b9d542588a4539e64c50393a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D1=80=D0=B8=D0=B9?= Date: Sat, 13 Aug 2022 23:44:59 +0300 Subject: [PATCH] add protect content global default (#917) --- aiogram/bot/base.py | 22 +++++++++++++++ aiogram/bot/bot.py | 38 +++++++++++++++++++++++++ aiogram/dispatcher/webhook.py | 52 +++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/aiogram/bot/base.py b/aiogram/bot/base.py index 8fd2094950..0c23668158 100644 --- a/aiogram/bot/base.py +++ b/aiogram/bot/base.py @@ -38,6 +38,7 @@ def __init__( validate_token: Optional[base.Boolean] = True, parse_mode: typing.Optional[base.String] = None, disable_web_page_preview: Optional[base.Boolean] = None, + protect_content: Optional[base.Boolean] = None, timeout: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] = None, server: TelegramAPIServer = TELEGRAM_PRODUCTION ): @@ -60,6 +61,9 @@ def __init__( :type parse_mode: :obj:`str` :param disable_web_page_preview: You can set default disable web page preview parameter :type disable_web_page_preview: :obj:`bool` + :param protect_content: Protects the contents of sent messages + from forwarding and saving + :type protect_content: :obj:`typing.Optional[base.Boolean]` :param timeout: Request timeout :type timeout: :obj:`typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]]` :param server: Telegram Bot API Server endpoint. @@ -111,6 +115,7 @@ def __init__( self.parse_mode = parse_mode self.disable_web_page_preview = disable_web_page_preview + self.protect_content = protect_content async def get_new_session(self) -> aiohttp.ClientSession: return aiohttp.ClientSession( @@ -361,5 +366,22 @@ def disable_web_page_preview(self, value): def disable_web_page_preview(self): self.disable_web_page_preview = None + @property + def protect_content(self): + return getattr(self, "_protect_content", None) + + @protect_content.setter + def protect_content(self, value): + if value is None: + setattr(self, "_protect_content", None) + return + if not isinstance(value, bool): + raise TypeError(f"Protect content must be bool, not {type(value)}") + setattr(self, "_protect_content", value) + + @protect_content.deleter + def protect_content(self): + self.protect_content = None + def check_auth_widget(self, data): return check_integrity(self.__token, data) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 0e82ad77c7..27c1ed6311 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -335,6 +335,8 @@ async def send_message(self, payload.setdefault('parse_mode', self.parse_mode) if self.disable_web_page_preview: payload.setdefault('disable_web_page_preview', self.disable_web_page_preview) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_MESSAGE, payload) return types.Message(**result) @@ -375,6 +377,8 @@ async def forward_message(self, :rtype: :obj:`types.Message` """ payload = generate_payload(**locals()) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.FORWARD_MESSAGE, payload) return types.Message(**result) @@ -457,6 +461,8 @@ async def copy_message(self, payload = generate_payload(**locals()) if self.parse_mode and caption_entities is None: payload.setdefault('parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.COPY_MESSAGE, payload) return types.MessageId(**result) @@ -525,6 +531,8 @@ async def send_photo(self, payload = generate_payload(**locals(), exclude=['photo']) if self.parse_mode and caption_entities is None: payload.setdefault('parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'photo', photo) @@ -615,6 +623,8 @@ async def send_audio(self, payload = generate_payload(**locals(), exclude=['audio', 'thumb']) if self.parse_mode and caption_entities is None: payload.setdefault('parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'audio', audio) @@ -705,6 +715,8 @@ async def send_document(self, payload = generate_payload(**locals(), exclude=['document']) if self.parse_mode and caption_entities is None: payload.setdefault('parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'document', document) @@ -797,6 +809,8 @@ async def send_video(self, chat_id: typing.Union[base.Integer, base.String], payload = generate_payload(**locals(), exclude=['video', 'thumb']) if self.parse_mode and caption_entities is None: payload.setdefault('parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'video', video) @@ -892,6 +906,8 @@ async def send_animation(self, payload = generate_payload(**locals(), exclude=["animation", "thumb"]) if self.parse_mode and caption_entities is None: payload.setdefault('parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'animation', animation) @@ -972,6 +988,8 @@ async def send_voice(self, payload = generate_payload(**locals(), exclude=['voice']) if self.parse_mode and caption_entities is None: payload.setdefault('parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'voice', voice) @@ -1038,6 +1056,8 @@ async def send_video_note(self, chat_id: typing.Union[base.Integer, base.String] """ reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals(), exclude=['video_note']) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'video_note', video_note) @@ -1101,6 +1121,8 @@ async def send_media_group(self, media = prepare_arg(media) payload = generate_payload(**locals(), exclude=['files']) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_MEDIA_GROUP, payload, files) return [types.Message(**message) for message in result] @@ -1174,6 +1196,8 @@ async def send_location(self, chat_id: typing.Union[base.Integer, base.String], """ reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals()) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_LOCATION, payload) return types.Message(**result) @@ -1352,6 +1376,8 @@ async def send_venue(self, """ reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals()) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_VENUE, payload) return types.Message(**result) @@ -1415,6 +1441,8 @@ async def send_contact(self, chat_id: typing.Union[base.Integer, base.String], payload = generate_payload(**locals()) result = await self.request(api.Methods.SEND_CONTACT, payload) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) return types.Message(**result) async def send_poll(self, @@ -1533,6 +1561,8 @@ async def send_poll(self, payload = generate_payload(**locals()) if self.parse_mode and explanation_entities is None: payload.setdefault('explanation_parse_mode', self.parse_mode) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_POLL, payload) return types.Message(**result) @@ -1592,6 +1622,8 @@ async def send_dice(self, reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals()) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_DICE, payload) return types.Message(**result) @@ -2966,6 +2998,8 @@ async def send_sticker(self, chat_id: typing.Union[base.Integer, base.String], """ reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals(), exclude=['sticker']) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) files = {} prepare_file(payload, files, 'sticker', sticker) @@ -3426,6 +3460,8 @@ async def send_invoice(self, reply_markup = prepare_arg(reply_markup) provider_data = prepare_arg(provider_data) payload_ = generate_payload(**locals()) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_INVOICE, payload_) return types.Message(**result) @@ -3631,6 +3667,8 @@ async def send_game(self, """ reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals()) + if self.protect_content is not None: + payload.setdefault('protect_content', self.protect_content) result = await self.request(api.Methods.SEND_GAME, payload) return types.Message(**result) diff --git a/aiogram/dispatcher/webhook.py b/aiogram/dispatcher/webhook.py index 31ef88f177..32987899db 100644 --- a/aiogram/dispatcher/webhook.py +++ b/aiogram/dispatcher/webhook.py @@ -458,6 +458,18 @@ def protect_content(self): setattr(self, "protect_content", True) return self + @staticmethod + def _global_protect_content(): + """ + Detect global protect content value + + :return: + """ + from aiogram import Bot + bot = Bot.get_current() + if bot is not None: + return bot.protect_content + class ParseModeMixin: def as_html(self): @@ -536,6 +548,8 @@ def __init__(self, chat_id: Union[Integer, String] = None, parse_mode = self._global_parse_mode() if disable_web_page_preview is None: disable_web_page_preview = self._global_disable_web_page_preview() + if protect_content is None: + protect_content = self._global_protect_content() self.chat_id = chat_id self.text = text @@ -607,6 +621,9 @@ def __init__(self, chat_id: Union[Integer, String] = None, from forwarding and saving :param message_id: Integer - Message identifier in the chat specified in from_chat_id """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.from_chat_id = from_chat_id self.message_id = message_id @@ -669,6 +686,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.photo = photo self.caption = caption @@ -731,6 +751,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.audio = audio self.caption = caption @@ -793,6 +816,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.document = document self.caption = caption @@ -856,6 +882,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.video = video self.duration = duration @@ -919,6 +948,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.voice = voice self.caption = caption @@ -977,6 +1009,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.video_note = video_note self.duration = duration @@ -1037,6 +1072,8 @@ def __init__(self, chat_id: Union[Integer, String], elif isinstance(media, list): # Convert list to MediaGroup media = types.MediaGroup(media) + if protect_content is None: + protect_content = self._global_protect_content() self.chat_id = chat_id self.media = media @@ -1117,6 +1154,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.latitude = latitude self.longitude = longitude @@ -1177,6 +1217,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.latitude = latitude self.longitude = longitude @@ -1237,6 +1280,9 @@ def __init__(self, chat_id: Union[Integer, String], - Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.phone_number = phone_number self.first_name = first_name @@ -1845,6 +1891,9 @@ def __init__(self, chat_id: Union[Integer, String], Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.sticker = sticker self.disable_notification = disable_notification @@ -2288,6 +2337,9 @@ def __init__(self, chat_id: Integer, :param reply_markup: types.InlineKeyboardMarkup (Optional) - A JSON-serialized object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown. If not empty, the first button must launch the game. """ + if protect_content is None: + protect_content = self._global_protect_content() + self.chat_id = chat_id self.game_short_name = game_short_name self.disable_notification = disable_notification