From 384f4eba71519e0da12c23226b0aac10a706bd8d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 29 Nov 2020 15:48:29 +0100 Subject: [PATCH] Add support for manual text entities. --- .../methods/messages/edit_message_caption.py | 7 +- .../methods/messages/edit_message_text.py | 9 +- pyrogram/methods/messages/send_animation.py | 8 +- pyrogram/methods/messages/send_audio.py | 11 +- pyrogram/methods/messages/send_document.py | 8 +- pyrogram/methods/messages/send_message.py | 10 +- pyrogram/methods/messages/send_photo.py | 8 +- pyrogram/methods/messages/send_video.py | 8 +- pyrogram/methods/messages/send_voice.py | 8 +- pyrogram/types/input_media/input_media.py | 12 +- .../input_media/input_media_animation.py | 9 +- .../types/input_media/input_media_audio.py | 9 +- .../types/input_media/input_media_document.py | 11 +- .../types/input_media/input_media_photo.py | 11 +- .../types/input_media/input_media_video.py | 9 +- pyrogram/types/messages_and_media/message.py | 50 ++++++++ .../messages_and_media/message_entity.py | 116 ++++++++++++++---- pyrogram/utils.py | 25 +++- 18 files changed, 270 insertions(+), 59 deletions(-) diff --git a/pyrogram/methods/messages/edit_message_caption.py b/pyrogram/methods/messages/edit_message_caption.py index 76b6cc48..a1cceffa 100644 --- a/pyrogram/methods/messages/edit_message_caption.py +++ b/pyrogram/methods/messages/edit_message_caption.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List from pyrogram import types from pyrogram.scaffold import Scaffold @@ -29,6 +29,7 @@ class EditMessageCaption(Scaffold): message_id: int, caption: str, parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, reply_markup: "types.InlineKeyboardMarkup" = None ) -> "types.Message": """Edit the caption of media messages. @@ -52,6 +53,9 @@ class EditMessageCaption(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -68,5 +72,6 @@ class EditMessageCaption(Scaffold): message_id=message_id, text=caption, parse_mode=parse_mode, + entities=caption_entities, reply_markup=reply_markup ) diff --git a/pyrogram/methods/messages/edit_message_text.py b/pyrogram/methods/messages/edit_message_text.py index b7d84830..ece20d04 100644 --- a/pyrogram/methods/messages/edit_message_text.py +++ b/pyrogram/methods/messages/edit_message_text.py @@ -16,10 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List from pyrogram import raw from pyrogram import types +from pyrogram import utils from pyrogram.scaffold import Scaffold @@ -30,6 +31,7 @@ class EditMessageText(Scaffold): message_id: int, text: str, parse_mode: Union[str, None] = object, + entities: List["types.MessageEntity"] = None, disable_web_page_preview: bool = None, reply_markup: "types.InlineKeyboardMarkup" = None ) -> "types.Message": @@ -54,6 +56,9 @@ class EditMessageText(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in message text, which can be specified instead of __parse_mode__. + disable_web_page_preview (``bool``, *optional*): Disables link previews for links in this message. @@ -81,7 +86,7 @@ class EditMessageText(Scaffold): id=message_id, no_webpage=disable_web_page_preview or None, reply_markup=reply_markup.write() if reply_markup else None, - **await self.parser.parse(text, parse_mode) + **await utils.parse_text_entities(self, text, parse_mode, entities) ) ) diff --git a/pyrogram/methods/messages/send_animation.py b/pyrogram/methods/messages/send_animation.py index 2abc8a92..e705b5c8 100644 --- a/pyrogram/methods/messages/send_animation.py +++ b/pyrogram/methods/messages/send_animation.py @@ -18,7 +18,7 @@ import os import re -from typing import Union, BinaryIO +from typing import Union, BinaryIO, List from pyrogram import StopTransmission from pyrogram import raw @@ -37,6 +37,7 @@ class SendAnimation(Scaffold): caption: str = "", unsave: bool = False, parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, width: int = 0, height: int = 0, @@ -83,6 +84,9 @@ class SendAnimation(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of sent animation in seconds. @@ -219,7 +223,7 @@ class SendAnimation(Scaffold): random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, - **await self.parser.parse(caption, parse_mode) + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) ) ) except FilePartMissing as e: diff --git a/pyrogram/methods/messages/send_audio.py b/pyrogram/methods/messages/send_audio.py index ceff4a54..18fd2bf1 100644 --- a/pyrogram/methods/messages/send_audio.py +++ b/pyrogram/methods/messages/send_audio.py @@ -18,7 +18,7 @@ import os import re -from typing import Union, BinaryIO +from typing import Union, BinaryIO, List from pyrogram import StopTransmission from pyrogram import raw @@ -36,10 +36,12 @@ class SendAudio(Scaffold): audio: Union[str, BinaryIO], caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, performer: str = None, title: str = None, - thumb: Union[str, BinaryIO] = None, file_name: str = None, + thumb: Union[str, BinaryIO] = None, + file_name: str = None, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, @@ -79,6 +81,9 @@ class SendAudio(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of the audio in seconds. @@ -213,7 +218,7 @@ class SendAudio(Scaffold): random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, - **await self.parser.parse(caption, parse_mode) + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) ) ) except FilePartMissing as e: diff --git a/pyrogram/methods/messages/send_document.py b/pyrogram/methods/messages/send_document.py index d64117d5..20fea5d5 100644 --- a/pyrogram/methods/messages/send_document.py +++ b/pyrogram/methods/messages/send_document.py @@ -18,7 +18,7 @@ import os import re -from typing import Union, BinaryIO +from typing import Union, BinaryIO, List from pyrogram import StopTransmission from pyrogram import raw @@ -37,6 +37,7 @@ class SendDocument(Scaffold): thumb: Union[str, BinaryIO] = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, file_name: str = None, force_document: bool = None, disable_notification: bool = None, @@ -82,6 +83,9 @@ class SendDocument(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + file_name (``str``, *optional*): File name of the document sent. Defaults to file's path basename. @@ -191,7 +195,7 @@ class SendDocument(Scaffold): random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, - **await self.parser.parse(caption, parse_mode) + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) ) ) except FilePartMissing as e: diff --git a/pyrogram/methods/messages/send_message.py b/pyrogram/methods/messages/send_message.py index 93264731..e873b58d 100644 --- a/pyrogram/methods/messages/send_message.py +++ b/pyrogram/methods/messages/send_message.py @@ -16,9 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List -from pyrogram import raw +from pyrogram import raw, utils from pyrogram import types from pyrogram.scaffold import Scaffold @@ -29,6 +29,7 @@ class SendMessage(Scaffold): chat_id: Union[int, str], text: str, parse_mode: Union[str, None] = object, + entities: List["types.MessageEntity"] = None, disable_web_page_preview: bool = None, disable_notification: bool = None, reply_to_message_id: int = None, @@ -58,6 +59,9 @@ class SendMessage(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in message text, which can be specified instead of __parse_mode__. + disable_web_page_preview (``bool``, *optional*): Disables link previews for links in this message. @@ -116,7 +120,7 @@ class SendMessage(Scaffold): ])) """ - message, entities = (await self.parser.parse(text, parse_mode)).values() + message, entities = (await utils.parse_text_entities(self, text, parse_mode, entities)).values() r = await self.send( raw.functions.messages.SendMessage( diff --git a/pyrogram/methods/messages/send_photo.py b/pyrogram/methods/messages/send_photo.py index 6caca1bf..0b84a8c0 100644 --- a/pyrogram/methods/messages/send_photo.py +++ b/pyrogram/methods/messages/send_photo.py @@ -18,7 +18,7 @@ import os import re -from typing import Union, BinaryIO +from typing import Union, BinaryIO, List import pyrogram from pyrogram import raw @@ -36,6 +36,7 @@ class SendPhoto(Scaffold): photo: Union[str, BinaryIO], caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, ttl_seconds: int = None, disable_notification: bool = None, reply_to_message_id: int = None, @@ -74,6 +75,9 @@ class SendPhoto(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + ttl_seconds (``int``, *optional*): Self-Destruct Timer. If you set a timer, the photo will self-destruct in *ttl_seconds* @@ -169,7 +173,7 @@ class SendPhoto(Scaffold): random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, - **await self.parser.parse(caption, parse_mode) + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) ) ) except FilePartMissing as e: diff --git a/pyrogram/methods/messages/send_video.py b/pyrogram/methods/messages/send_video.py index cc4e082d..18eb5b67 100644 --- a/pyrogram/methods/messages/send_video.py +++ b/pyrogram/methods/messages/send_video.py @@ -18,7 +18,7 @@ import os import re -from typing import Union, BinaryIO +from typing import Union, BinaryIO, List from pyrogram import StopTransmission from pyrogram import raw @@ -36,6 +36,7 @@ class SendVideo(Scaffold): video: Union[str, BinaryIO], caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, width: int = 0, height: int = 0, @@ -79,6 +80,9 @@ class SendVideo(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of sent video in seconds. @@ -213,7 +217,7 @@ class SendVideo(Scaffold): random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, - **await self.parser.parse(caption, parse_mode) + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) ) ) except FilePartMissing as e: diff --git a/pyrogram/methods/messages/send_voice.py b/pyrogram/methods/messages/send_voice.py index d6dd9bdb..36bea41c 100644 --- a/pyrogram/methods/messages/send_voice.py +++ b/pyrogram/methods/messages/send_voice.py @@ -18,7 +18,7 @@ import os import re -from typing import Union, BinaryIO +from typing import Union, BinaryIO, List from pyrogram import StopTransmission from pyrogram import raw @@ -36,6 +36,7 @@ class SendVoice(Scaffold): voice: Union[str, BinaryIO], caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, disable_notification: bool = None, reply_to_message_id: int = None, @@ -74,6 +75,9 @@ class SendVoice(Scaffold): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of the voice message in seconds. @@ -175,7 +179,7 @@ class SendVoice(Scaffold): random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, - **await self.parser.parse(caption, parse_mode) + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) ) ) except FilePartMissing as e: diff --git a/pyrogram/types/input_media/input_media.py b/pyrogram/types/input_media/input_media.py index 6c739d87..bcbe2e52 100644 --- a/pyrogram/types/input_media/input_media.py +++ b/pyrogram/types/input_media/input_media.py @@ -16,6 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from typing import List + +from ..messages_and_media import MessageEntity from ..object import Object @@ -31,9 +34,16 @@ class InputMedia(Object): - :obj:`~pyrogram.types.InputMediaVideo` """ - def __init__(self, media: str, caption: str, parse_mode: str): + def __init__( + self, + media: str, + caption: str = None, + parse_mode: str = None, + caption_entities: List[MessageEntity] = None + ): super().__init__() self.media = media self.caption = caption self.parse_mode = parse_mode + self.caption_entities = caption_entities diff --git a/pyrogram/types/input_media/input_media_animation.py b/pyrogram/types/input_media/input_media_animation.py index a59f229f..bd9ac1b5 100644 --- a/pyrogram/types/input_media/input_media_animation.py +++ b/pyrogram/types/input_media/input_media_animation.py @@ -16,9 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List from .input_media import InputMedia +from ..messages_and_media import MessageEntity class InputMediaAnimation(InputMedia): @@ -46,6 +47,9 @@ class InputMediaAnimation(InputMedia): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + width (``int``, *optional*): Animation width. @@ -62,11 +66,12 @@ class InputMediaAnimation(InputMedia): thumb: str = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List[MessageEntity] = None, width: int = 0, height: int = 0, duration: int = 0 ): - super().__init__(media, caption, parse_mode) + super().__init__(media, caption, parse_mode, caption_entities) self.thumb = thumb self.width = width diff --git a/pyrogram/types/input_media/input_media_audio.py b/pyrogram/types/input_media/input_media_audio.py index 99fa973c..be966d52 100644 --- a/pyrogram/types/input_media/input_media_audio.py +++ b/pyrogram/types/input_media/input_media_audio.py @@ -16,9 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List from .input_media import InputMedia +from ..messages_and_media import MessageEntity class InputMediaAudio(InputMedia): @@ -48,6 +49,9 @@ class InputMediaAudio(InputMedia): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of the audio in seconds @@ -64,11 +68,12 @@ class InputMediaAudio(InputMedia): thumb: str = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List[MessageEntity] = None, duration: int = 0, performer: str = "", title: str = "" ): - super().__init__(media, caption, parse_mode) + super().__init__(media, caption, parse_mode, caption_entities) self.thumb = thumb self.duration = duration diff --git a/pyrogram/types/input_media/input_media_document.py b/pyrogram/types/input_media/input_media_document.py index 23deec32..7d6500d4 100644 --- a/pyrogram/types/input_media/input_media_document.py +++ b/pyrogram/types/input_media/input_media_document.py @@ -16,9 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List from .input_media import InputMedia +from ..messages_and_media import MessageEntity class InputMediaDocument(InputMedia): @@ -45,6 +46,9 @@ class InputMediaDocument(InputMedia): Pass "markdown" or "md" to enable Markdown-style parsing only. Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. """ def __init__( @@ -52,8 +56,9 @@ class InputMediaDocument(InputMedia): media: str, thumb: str = None, caption: str = "", - parse_mode: Union[str, None] = object + parse_mode: Union[str, None] = object, + caption_entities: List[MessageEntity] = None ): - super().__init__(media, caption, parse_mode) + super().__init__(media, caption, parse_mode, caption_entities) self.thumb = thumb diff --git a/pyrogram/types/input_media/input_media_photo.py b/pyrogram/types/input_media/input_media_photo.py index f8797982..7b0c97aa 100644 --- a/pyrogram/types/input_media/input_media_photo.py +++ b/pyrogram/types/input_media/input_media_photo.py @@ -16,9 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List from .input_media import InputMedia +from ..messages_and_media import MessageEntity class InputMediaPhoto(InputMedia): @@ -41,12 +42,16 @@ class InputMediaPhoto(InputMedia): Pass "markdown" or "md" to enable Markdown-style parsing only. Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. """ def __init__( self, media: str, caption: str = "", - parse_mode: Union[str, None] = object + parse_mode: Union[str, None] = object, + caption_entities: List[MessageEntity] = None ): - super().__init__(media, caption, parse_mode) + super().__init__(media, caption, parse_mode, caption_entities) diff --git a/pyrogram/types/input_media/input_media_video.py b/pyrogram/types/input_media/input_media_video.py index 3de713e1..8a3013bd 100644 --- a/pyrogram/types/input_media/input_media_video.py +++ b/pyrogram/types/input_media/input_media_video.py @@ -16,9 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union +from typing import Union, List from .input_media import InputMedia +from ..messages_and_media import MessageEntity class InputMediaVideo(InputMedia): @@ -48,6 +49,9 @@ class InputMediaVideo(InputMedia): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + width (``int``, *optional*): Video width. @@ -67,12 +71,13 @@ class InputMediaVideo(InputMedia): thumb: str = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List[MessageEntity] = None, width: int = 0, height: int = 0, duration: int = 0, supports_streaming: bool = True ): - super().__init__(media, caption, parse_mode) + super().__init__(media, caption, parse_mode, caption_entities) self.thumb = thumb self.width = width diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index 1f85ba8b..4992c37d 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -711,6 +711,7 @@ class Message(Object, Update): text: str, quote: bool = None, parse_mode: Union[str, None] = object, + entities: List["types.MessageEntity"] = None, disable_web_page_preview: bool = None, disable_notification: bool = None, reply_to_message_id: int = None, @@ -749,6 +750,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in message text, which can be specified instead of __parse_mode__. + disable_web_page_preview (``bool``, *optional*): Disables link previews for links in this message. @@ -779,6 +783,7 @@ class Message(Object, Update): chat_id=self.chat.id, text=text, parse_mode=parse_mode, + entities=entities, disable_web_page_preview=disable_web_page_preview, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, @@ -793,6 +798,7 @@ class Message(Object, Update): quote: bool = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, width: int = 0, height: int = 0, @@ -846,6 +852,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of sent animation in seconds. @@ -913,6 +922,7 @@ class Message(Object, Update): animation=animation, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, duration=duration, width=width, height=height, @@ -930,6 +940,7 @@ class Message(Object, Update): quote: bool = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, performer: str = None, title: str = None, @@ -983,6 +994,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of the audio in seconds. @@ -1050,6 +1064,7 @@ class Message(Object, Update): audio=audio, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, duration=duration, performer=performer, title=title, @@ -1067,6 +1082,7 @@ class Message(Object, Update): quote: bool = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, disable_notification: bool = None, reply_to_message_id: int = None, reply_markup: Union[ @@ -1112,6 +1128,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. @@ -1140,6 +1159,7 @@ class Message(Object, Update): file_id=file_id, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, reply_markup=reply_markup @@ -1275,6 +1295,7 @@ class Message(Object, Update): thumb: str = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, disable_notification: bool = None, reply_to_message_id: int = None, reply_markup: Union[ @@ -1330,6 +1351,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. @@ -1383,6 +1407,7 @@ class Message(Object, Update): thumb=thumb, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, reply_markup=reply_markup, @@ -1670,6 +1695,7 @@ class Message(Object, Update): quote: bool = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, ttl_seconds: int = None, disable_notification: bool = None, reply_to_message_id: int = None, @@ -1720,6 +1746,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + ttl_seconds (``int``, *optional*): Self-Destruct Timer. If you set a timer, the photo will self-destruct in *ttl_seconds* @@ -1777,6 +1806,7 @@ class Message(Object, Update): photo=photo, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, ttl_seconds=ttl_seconds, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, @@ -2093,6 +2123,7 @@ class Message(Object, Update): quote: bool = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, width: int = 0, height: int = 0, @@ -2147,6 +2178,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of sent video in seconds. @@ -2217,6 +2251,7 @@ class Message(Object, Update): video=video, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, duration=duration, width=width, height=height, @@ -2353,6 +2388,7 @@ class Message(Object, Update): quote: bool = None, caption: str = "", parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, duration: int = 0, disable_notification: bool = None, reply_to_message_id: int = None, @@ -2403,6 +2439,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + duration (``int``, *optional*): Duration of the voice message in seconds. @@ -2458,6 +2497,7 @@ class Message(Object, Update): voice=voice, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, duration=duration, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, @@ -2470,6 +2510,7 @@ class Message(Object, Update): self, text: str, parse_mode: Union[str, None] = object, + entities: List["types.MessageEntity"] = None, disable_web_page_preview: bool = None, reply_markup: "types.InlineKeyboardMarkup" = None ) -> "Message": @@ -2501,6 +2542,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in message text, which can be specified instead of __parse_mode__. + disable_web_page_preview (``bool``, *optional*): Disables link previews for links in this message. @@ -2518,6 +2562,7 @@ class Message(Object, Update): message_id=self.message_id, text=text, parse_mode=parse_mode, + entities=entities, disable_web_page_preview=disable_web_page_preview, reply_markup=reply_markup ) @@ -2528,6 +2573,7 @@ class Message(Object, Update): self, caption: str, parse_mode: Union[str, None] = object, + caption_entities: List["types.MessageEntity"] = None, reply_markup: "types.InlineKeyboardMarkup" = None ) -> "Message": """Bound method *edit_caption* of :obj:`~pyrogram.types.Message`. @@ -2558,6 +2604,9 @@ class Message(Object, Update): Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of __parse_mode__. + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. @@ -2572,6 +2621,7 @@ class Message(Object, Update): message_id=self.message_id, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, reply_markup=reply_markup ) diff --git a/pyrogram/types/messages_and_media/message_entity.py b/pyrogram/types/messages_and_media/message_entity.py index a88a91c8..0dd13d70 100644 --- a/pyrogram/types/messages_and_media/message_entity.py +++ b/pyrogram/types/messages_and_media/message_entity.py @@ -16,12 +16,60 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from enum import Enum, auto + import pyrogram from pyrogram import raw from pyrogram import types from ..object import Object +class AutoName(Enum): + def _generate_next_value_(self, *args): + return self.lower() + + +class MessageEntityType(AutoName): + MENTION = auto() + HASHTAG = auto() + CASHTAG = auto() + BOT_COMMAND = auto() + URL = auto() + EMAIL = auto() + PHONE_NUMBER = auto() + BOLD = auto() + ITALIC = auto() + UNDERLINE = auto() + STRIKETHROUGH = auto() + CODE = auto() + PRE = auto() + TEXT_LINK = auto() + TEXT_MENTION = auto() + BLOCKQUOTE = auto() + + +RAW_ENTITIES_TO_TYPE = { + raw.types.MessageEntityMention: MessageEntityType.MENTION, + raw.types.MessageEntityHashtag: MessageEntityType.HASHTAG, + raw.types.MessageEntityCashtag: MessageEntityType.CASHTAG, + raw.types.MessageEntityBotCommand: MessageEntityType.BOT_COMMAND, + raw.types.MessageEntityUrl: MessageEntityType.URL, + raw.types.MessageEntityEmail: MessageEntityType.EMAIL, + raw.types.MessageEntityBold: MessageEntityType.BOLD, + raw.types.MessageEntityItalic: MessageEntityType.ITALIC, + raw.types.MessageEntityCode: MessageEntityType.CODE, + raw.types.MessageEntityPre: MessageEntityType.PRE, + raw.types.MessageEntityUnderline: MessageEntityType.UNDERLINE, + raw.types.MessageEntityStrike: MessageEntityType.STRIKETHROUGH, + raw.types.MessageEntityBlockquote: MessageEntityType.BLOCKQUOTE, + raw.types.MessageEntityTextUrl: MessageEntityType.TEXT_LINK, + raw.types.MessageEntityMentionName: MessageEntityType.TEXT_MENTION, + raw.types.MessageEntityPhone: MessageEntityType.PHONE_NUMBER +} + +TYPE_TO_RAW_ENTITIES = {v.value: k for k, v in RAW_ENTITIES_TO_TYPE.items()} + + class MessageEntity(Object): """One special entity in a text message. For example, hashtags, usernames, URLs, etc. @@ -29,9 +77,12 @@ class MessageEntity(Object): Parameters: type (``str``): Type of the entity. - Can be "mention" (@username), "hashtag", "cashtag", "bot_command", "url", "email", "phone_number", "bold" - (bold text), "italic" (italic text), "code" (monowidth string), "pre" (monowidth block), "text_link" - (for clickable text URLs), "text_mention" (for custom text mentions based on users' identifiers). + Can be "mention" (``@username``), "hashtag" (``#hashtag``), "cashtag" (``$PYRO``), + "bot_command" (``/start@pyrogrambot``), "url" (``https://pyrogram.org``), + "email" (``do-not-reply@pyrogram.org``), "phone_number" (``+1-420-069-1337``), "bold" (**bold text**), + "italic" (*italic text*), "underline" (underlined text), "strikethrough" (strikethrough text), + "code" (monowidth string), "pre" (monowidth block), "text_link" (for clickable text URLs), + "text_mention" (for users without usernames). offset (``int``): Offset in UTF-16 code units to the start of the entity. @@ -44,26 +95,10 @@ class MessageEntity(Object): user (:obj:`~pyrogram.types.User`, *optional*): For "text_mention" only, the mentioned user. - """ - ENTITIES = { - raw.types.MessageEntityMention.ID: "mention", - raw.types.MessageEntityHashtag.ID: "hashtag", - raw.types.MessageEntityCashtag.ID: "cashtag", - raw.types.MessageEntityBotCommand.ID: "bot_command", - raw.types.MessageEntityUrl.ID: "url", - raw.types.MessageEntityEmail.ID: "email", - raw.types.MessageEntityBold.ID: "bold", - raw.types.MessageEntityItalic.ID: "italic", - raw.types.MessageEntityCode.ID: "code", - raw.types.MessageEntityPre.ID: "pre", - raw.types.MessageEntityUnderline.ID: "underline", - raw.types.MessageEntityStrike.ID: "strike", - raw.types.MessageEntityBlockquote.ID: "blockquote", - raw.types.MessageEntityTextUrl.ID: "text_link", - raw.types.MessageEntityMentionName.ID: "text_mention", - raw.types.MessageEntityPhone.ID: "phone_number" - } + language (``str``. *optional*): + For "pre" only, the programming language of the entity text. + """ def __init__( self, @@ -73,7 +108,8 @@ class MessageEntity(Object): offset: int, length: int, url: str = None, - user: "types.User" = None + user: "types.User" = None, + language: str = None ): super().__init__(client) @@ -82,19 +118,49 @@ class MessageEntity(Object): self.length = length self.url = url self.user = user + self.language = language @staticmethod def _parse(client, entity, users: dict) -> "MessageEntity" or None: - type = MessageEntity.ENTITIES.get(entity.ID, None) + type = RAW_ENTITIES_TO_TYPE.get(entity.__class__, None) if type is None: return None return MessageEntity( - type=type, + type=type.value, offset=entity.offset, length=entity.length, url=getattr(entity, "url", None), user=types.User._parse(client, users.get(getattr(entity, "user_id", None), None)), + language=getattr(entity, "language", None), client=client ) + + async def write(self): + args = self.__dict__.copy() + + for arg in ("_client", "type", "user"): + args.pop(arg) + + if self.user: + args["user_id"] = await self._client.resolve_peer(self.user.id) + + if not self.url: + args.pop("url") + + if self.language is None: + args.pop("language") + + try: + entity = TYPE_TO_RAW_ENTITIES[self.type] + + if entity is raw.types.MessageEntityMentionName: + entity = raw.types.InputMessageEntityMentionName + except KeyError as e: + raise ValueError(f"Invalid message entity type {e}") + else: + try: + return entity(**args) + except TypeError as e: + raise TypeError(f"{entity.QUALNAME}'s {e}") diff --git a/pyrogram/utils.py b/pyrogram/utils.py index e0b79786..4c444533 100644 --- a/pyrogram/utils.py +++ b/pyrogram/utils.py @@ -24,9 +24,9 @@ import os import struct from concurrent.futures.thread import ThreadPoolExecutor from getpass import getpass -from typing import List -from typing import Union +from typing import Union, List, Dict +import pyrogram from pyrogram import raw from pyrogram import types from pyrogram.file_id import FileId, FileType, PHOTO_TYPES, DOCUMENT_TYPES @@ -294,3 +294,24 @@ def compute_password_check(r: raw.types.account.Password, password: str) -> raw. ) return raw.types.InputCheckPasswordSRP(srp_id=srp_id, A=A_bytes, M1=M1_bytes) + + +async def parse_text_entities( + client: "pyrogram.Client", + text: str, + parse_mode: str, + entities: List["types.MessageEntity"] +) -> Dict[str, raw.base.MessageEntity]: + if entities: + # Inject the client instance because parsing user mentions requires it + for entity in entities: + entity._client = client + + text, entities = text, [await entity.write() for entity in entities] + else: + text, entities = (await client.parser.parse(text, parse_mode)).values() + + return { + "message": text, + "entities": entities + }