Allow setting a per-client default parse mode

This commit is contained in:
Lonami Exo 2018-06-03 11:29:48 +02:00
parent 12812ea542
commit 8b16023566
3 changed files with 97 additions and 41 deletions

View File

@ -81,7 +81,7 @@ from .tl.types import (
InputUserSelf, UserProfilePhoto, ChatPhoto, UpdateMessageID, InputUserSelf, UserProfilePhoto, ChatPhoto, UpdateMessageID,
UpdateNewChannelMessage, UpdateNewMessage, UpdateShortSentMessage, UpdateNewChannelMessage, UpdateNewMessage, UpdateShortSentMessage,
PeerUser, InputPeerUser, InputPeerChat, InputPeerChannel, MessageEmpty, PeerUser, InputPeerUser, InputPeerChat, InputPeerChannel, MessageEmpty,
ChatInvite, ChatInviteAlready, PeerChannel, Photo, InputPeerSelf, ChatInvite, ChatInviteAlready, Photo, InputPeerSelf,
InputSingleMedia, InputMediaPhoto, InputPhoto, InputFile, InputFileBig, InputSingleMedia, InputMediaPhoto, InputPhoto, InputFile, InputFileBig,
InputDocument, InputMediaDocument, Document, MessageEntityTextUrl, InputDocument, InputMediaDocument, Document, MessageEntityTextUrl,
InputMessageEntityMentionName, DocumentAttributeVideo, InputMessageEntityMentionName, DocumentAttributeVideo,
@ -94,6 +94,7 @@ from .tl.types import (
from .tl.types.messages import DialogsSlice from .tl.types.messages import DialogsSlice
from .tl.types.account import PasswordInputSettings, NoPassword from .tl.types.account import PasswordInputSettings, NoPassword
from .tl import custom from .tl import custom
from .utils import Default
from .extensions import markdown, html from .extensions import markdown, html
__log__ = logging.getLogger(__name__) __log__ = logging.getLogger(__name__)
@ -200,6 +201,9 @@ class TelegramClient(TelegramBareClient):
self._event_builders = [] self._event_builders = []
self._events_pending_resolve = [] self._events_pending_resolve = []
# Default parse mode
self._parse_mode = markdown
# Some fields to easy signing in. Let {phone: hash} be # Some fields to easy signing in. Let {phone: hash} be
# a dictionary because the user may change their mind. # a dictionary because the user may change their mind.
self._phone_code_hash = {} self._phone_code_hash = {}
@ -704,26 +708,78 @@ class TelegramClient(TelegramBareClient):
if found: if found:
return custom.Message(self, found, entities, input_chat) return custom.Message(self, found, entities, input_chat)
@property
def parse_mode(self):
"""
This property is the default parse mode used when sending messages.
Defaults to `telethon.extensions.markdown`. It will always
be either ``None`` or an object with ``parse`` and ``unparse``
methods.
When setting a different value it should be one of:
* Object with ``parse`` and ``unparse`` methods.
* A ``callable`` to act as the parse method.
* A ``str`` indicating the ``parse_mode``. For Markdown ``'md'``
or ``'markdown'`` may be used. For HTML, ``'htm'`` or ``'html'``
may be used.
The ``parse`` method should be a function accepting a single
parameter, the text to parse, and returning a tuple consisting
of ``(parsed message str, [MessageEntity instances])``.
The ``unparse`` method should be the inverse of ``parse`` such
that ``assert text == unparse(*parse(text))``.
See :tl:`MessageEntity` for allowed message entities.
"""
return self._parse_mode
@parse_mode.setter
def parse_mode(self, mode):
self._parse_mode = self._sanitize_parse_mode(mode)
@staticmethod
def _sanitize_parse_mode(mode):
if not mode:
return None
if callable(mode):
class CustomMode:
@staticmethod
def unparse(text, entities):
raise NotImplementedError
CustomMode.parse = mode
return CustomMode
elif (all(hasattr(mode, x) for x in ('parse', 'unparse'))
and all(callable(x) for x in (mode.parse, mode.unparse))):
return mode
elif isinstance(mode, str):
try:
return {
'md': markdown,
'markdown': markdown,
'htm': html,
'html': html
}[mode.lower()]
except KeyError:
raise ValueError('Unknown parse mode {}'.format(mode))
else:
raise TypeError('Invalid parse mode type {}'.format(mode))
def _parse_message_text(self, message, parse_mode): def _parse_message_text(self, message, parse_mode):
""" """
Returns a (parsed message, entities) tuple depending on ``parse_mode``. Returns a (parsed message, entities) tuple depending on ``parse_mode``.
""" """
if parse_mode == Default:
parse_mode = self._parse_mode
else:
parse_mode = self._sanitize_parse_mode(parse_mode)
if not parse_mode: if not parse_mode:
return message, [] return message, []
if isinstance(parse_mode, str): message, msg_entities = parse_mode.parse(message)
parse_mode = parse_mode.lower()
if parse_mode in {'md', 'markdown'}:
message, msg_entities = markdown.parse(message)
elif parse_mode.startswith('htm'):
message, msg_entities = html.parse(message)
else:
raise ValueError('Unknown parsing mode: {}'.format(parse_mode))
elif callable(parse_mode):
message, msg_entities = parse_mode(message)
else:
raise TypeError('Invalid parsing mode type: {}'.format(parse_mode))
for i, e in enumerate(msg_entities): for i, e in enumerate(msg_entities):
if isinstance(e, MessageEntityTextUrl): if isinstance(e, MessageEntityTextUrl):
m = re.match(r'^@|\+|tg://user\?id=(\d+)', e.url) m = re.match(r'^@|\+|tg://user\?id=(\d+)', e.url)
@ -740,9 +796,9 @@ class TelegramClient(TelegramBareClient):
return message, msg_entities return message, msg_entities
def send_message(self, entity, message='', reply_to=None, parse_mode='md', def send_message(self, entity, message='', reply_to=None,
link_preview=True, file=None, force_document=False, parse_mode=Default, link_preview=True, file=None,
clear_draft=False): force_document=False, clear_draft=False):
""" """
Sends the given message to the specified entity (user/chat/channel). Sends the given message to the specified entity (user/chat/channel).
@ -773,17 +829,9 @@ class TelegramClient(TelegramBareClient):
Whether to reply to a message or not. If an integer is provided, Whether to reply to a message or not. If an integer is provided,
it should be the ID of the message that it should reply to. it should be the ID of the message that it should reply to.
parse_mode (`str`, optional): parse_mode (`object`, optional):
Can be 'md' or 'markdown' for markdown-like parsing (default), See the `TelegramClient.parse_mode` property for allowed
or 'htm' or 'html' for HTML-like parsing. If ``None`` or any values. Markdown parsing will be used by default.
other false-y value is provided, the message will be sent with
no formatting.
If a ``callable`` is passed, it should be a function accepting
a `str` as an input and return as output a tuple consisting
of ``(parsed message str, [MessageEntity instances])``.
See :tl:`MessageEntity` for allowed message entities.
link_preview (`bool`, optional): link_preview (`bool`, optional):
Should the link preview be shown? Should the link preview be shown?
@ -925,8 +973,8 @@ class TelegramClient(TelegramBareClient):
result = [id_to_message[random_to_id[rnd]] for rnd in req.random_id] result = [id_to_message[random_to_id[rnd]] for rnd in req.random_id]
return result[0] if single else result return result[0] if single else result
def edit_message(self, entity, message=None, text=None, parse_mode='md', def edit_message(self, entity, message=None, text=None,
link_preview=True): parse_mode=Default, link_preview=True):
""" """
Edits the given message ID (to change its contents or disable preview). Edits the given message ID (to change its contents or disable preview).
@ -946,11 +994,9 @@ class TelegramClient(TelegramBareClient):
The new text of the message. Does nothing if the `entity` The new text of the message. Does nothing if the `entity`
was a :tl:`Message`. was a :tl:`Message`.
parse_mode (`str`, optional): parse_mode (`object`, optional):
Can be 'md' or 'markdown' for markdown-like parsing (default), See the `TelegramClient.parse_mode` property for allowed
or 'htm' or 'html' for HTML-like parsing. If ``None`` or any values. Markdown parsing will be used by default.
other false-y value is provided, the message will be sent with
no formatting.
link_preview (`bool`, optional): link_preview (`bool`, optional):
Should the link preview be shown? Should the link preview be shown?
@ -1531,7 +1577,7 @@ class TelegramClient(TelegramBareClient):
attributes=None, attributes=None,
thumb=None, thumb=None,
allow_cache=True, allow_cache=True,
parse_mode='md', parse_mode=Default,
voice_note=False, voice_note=False,
video_note=False, video_note=False,
**kwargs): **kwargs):
@ -1584,8 +1630,9 @@ class TelegramClient(TelegramBareClient):
Must be ``False`` if you wish to use different attributes Must be ``False`` if you wish to use different attributes
or thumb than those that were used when the file was cached. or thumb than those that were used when the file was cached.
parse_mode (`str`, optional): parse_mode (`object`, optional):
The parse mode for the caption message. See the `TelegramClient.parse_mode` property for allowed
values. Markdown parsing will be used by default.
voice_note (`bool`, optional): voice_note (`bool`, optional):
If ``True`` the audio will be sent as a voice note. If ``True`` the audio will be sent as a voice note.
@ -1788,7 +1835,7 @@ class TelegramClient(TelegramBareClient):
def _send_album(self, entity, files, caption='', def _send_album(self, entity, files, caption='',
progress_callback=None, reply_to=None, progress_callback=None, reply_to=None,
parse_mode='md'): parse_mode=Default):
"""Specialized version of .send_file for albums""" """Specialized version of .send_file for albums"""
# We don't care if the user wants to avoid cache, we will use it # We don't care if the user wants to avoid cache, we will use it
# anyway. Why? The cached version will be exactly the same thing # anyway. Why? The cached version will be exactly the same thing

View File

@ -5,6 +5,7 @@ from ..functions.messages import SaveDraftRequest
from ..types import UpdateDraftMessage, DraftMessage from ..types import UpdateDraftMessage, DraftMessage
from ...errors import RPCError from ...errors import RPCError
from ...extensions import markdown from ...extensions import markdown
from ...utils import Default
class Draft: class Draft:
@ -82,7 +83,7 @@ class Draft:
""" """
return not self._text return not self._text
def set_message(self, text=None, reply_to=0, parse_mode='md', def set_message(self, text=None, reply_to=0, parse_mode=Default,
link_preview=None): link_preview=None):
""" """
Changes the draft message on the Telegram servers. The changes are Changes the draft message on the Telegram servers. The changes are
@ -127,7 +128,7 @@ class Draft:
return result return result
def send(self, clear=True, parse_mode='md'): def send(self, clear=True, parse_mode=Default):
""" """
Sends the contents of this draft to the dialog. This is just a Sends the contents of this draft to the dialog. This is just a
wrapper around ``send_message(dialog.input_entity, *args, **kwargs)``. wrapper around ``send_message(dialog.input_entity, *args, **kwargs)``.

View File

@ -46,6 +46,14 @@ VALID_USERNAME_RE = re.compile(
) )
class Default:
"""
Sentinel value to indicate that the default value should be used.
Currently used for the ``parse_mode``, where a ``None`` mode should
be considered different from using the default.
"""
def get_display_name(entity): def get_display_name(entity):
""" """
Gets the display name for the given entity, if it's an :tl:`User`, Gets the display name for the given entity, if it's an :tl:`User`,