mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-07-13 09:32:27 +03:00
Separate parse message methods from uploads
This commit is contained in:
parent
83a024656c
commit
317b7053a0
129
telethon/client/messageparse.py
Normal file
129
telethon/client/messageparse.py
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import itertools
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .users import UserMethods
|
||||||
|
from .. import utils
|
||||||
|
from ..tl import types, custom
|
||||||
|
|
||||||
|
|
||||||
|
class MessageParseMethods(UserMethods):
|
||||||
|
|
||||||
|
# region Public properties
|
||||||
|
|
||||||
|
@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 = utils.sanitize_parse_mode(mode)
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region Private methods
|
||||||
|
|
||||||
|
async def _parse_message_text(self, message, parse_mode):
|
||||||
|
"""
|
||||||
|
Returns a (parsed message, entities) tuple depending on ``parse_mode``.
|
||||||
|
"""
|
||||||
|
if parse_mode == utils.Default:
|
||||||
|
parse_mode = self._parse_mode
|
||||||
|
else:
|
||||||
|
parse_mode = utils.sanitize_parse_mode(parse_mode)
|
||||||
|
|
||||||
|
if not parse_mode:
|
||||||
|
return message, []
|
||||||
|
|
||||||
|
message, msg_entities = parse_mode.parse(message)
|
||||||
|
for i, e in enumerate(msg_entities):
|
||||||
|
if isinstance(e, types.MessageEntityTextUrl):
|
||||||
|
m = re.match(r'^@|\+|tg://user\?id=(\d+)', e.url)
|
||||||
|
if m:
|
||||||
|
try:
|
||||||
|
msg_entities[i] = types.InputMessageEntityMentionName(
|
||||||
|
e.offset, e.length, await self.get_input_entity(
|
||||||
|
int(m.group(1)) if m.group(1) else e.url
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
# Make no replacement
|
||||||
|
pass
|
||||||
|
|
||||||
|
return message, msg_entities
|
||||||
|
|
||||||
|
def _get_response_message(self, request, result, input_chat):
|
||||||
|
"""
|
||||||
|
Extracts the response message known a request and Update result.
|
||||||
|
The request may also be the ID of the message to match.
|
||||||
|
"""
|
||||||
|
# Telegram seems to send updateMessageID first, then updateNewMessage,
|
||||||
|
# however let's not rely on that just in case.
|
||||||
|
if isinstance(request, int):
|
||||||
|
msg_id = request
|
||||||
|
else:
|
||||||
|
msg_id = None
|
||||||
|
for update in result.updates:
|
||||||
|
if isinstance(update, types.UpdateMessageID):
|
||||||
|
if update.random_id == request.random_id:
|
||||||
|
msg_id = update.id
|
||||||
|
break
|
||||||
|
|
||||||
|
if isinstance(result, types.UpdateShort):
|
||||||
|
updates = [result.update]
|
||||||
|
entities = {}
|
||||||
|
elif isinstance(result, (types.Updates, types.UpdatesCombined)):
|
||||||
|
updates = result.updates
|
||||||
|
entities = {utils.get_peer_id(x): x
|
||||||
|
for x in
|
||||||
|
itertools.chain(result.users, result.chats)}
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
found = None
|
||||||
|
for update in updates:
|
||||||
|
if isinstance(update, (
|
||||||
|
types.UpdateNewChannelMessage, types.UpdateNewMessage)):
|
||||||
|
if update.message.id == msg_id:
|
||||||
|
found = update.message
|
||||||
|
break
|
||||||
|
|
||||||
|
elif (isinstance(update, types.UpdateEditMessage)
|
||||||
|
and not isinstance(request.peer, types.InputPeerChannel)):
|
||||||
|
if request.id == update.message.id:
|
||||||
|
found = update.message
|
||||||
|
break
|
||||||
|
|
||||||
|
elif (isinstance(update, types.UpdateEditChannelMessage)
|
||||||
|
and utils.get_peer_id(request.peer) ==
|
||||||
|
utils.get_peer_id(update.message.to_id)):
|
||||||
|
if request.id == update.message.id:
|
||||||
|
found = update.message
|
||||||
|
break
|
||||||
|
|
||||||
|
if found:
|
||||||
|
return custom.Message(self, found, entities, input_chat)
|
||||||
|
|
||||||
|
# endregion
|
|
@ -5,6 +5,7 @@ import time
|
||||||
import warnings
|
import warnings
|
||||||
from collections import UserList
|
from collections import UserList
|
||||||
|
|
||||||
|
from .messageparse import MessageParseMethods
|
||||||
from .uploads import UploadMethods
|
from .uploads import UploadMethods
|
||||||
from .. import utils
|
from .. import utils
|
||||||
from ..tl import types, functions, custom
|
from ..tl import types, functions, custom
|
||||||
|
@ -12,7 +13,7 @@ from ..tl import types, functions, custom
|
||||||
__log__ = logging.getLogger(__name__)
|
__log__ = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MessageMethods(UploadMethods):
|
class MessageMethods(UploadMethods, MessageParseMethods):
|
||||||
|
|
||||||
# region Public methods
|
# region Public methods
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import io
|
import io
|
||||||
import itertools
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import warnings
|
import warnings
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
|
|
||||||
|
from .messageparse import MessageParseMethods
|
||||||
from .users import UserMethods
|
from .users import UserMethods
|
||||||
from .. import utils, helpers
|
from .. import utils, helpers
|
||||||
from ..extensions import markdown, html
|
|
||||||
from ..tl import types, functions, custom
|
from ..tl import types, functions, custom
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -23,7 +21,7 @@ except ImportError:
|
||||||
__log__ = logging.getLogger(__name__)
|
__log__ = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class UploadMethods(UserMethods):
|
class UploadMethods(MessageParseMethods, UserMethods):
|
||||||
|
|
||||||
# region Public methods
|
# region Public methods
|
||||||
|
|
||||||
|
@ -356,151 +354,6 @@ class UploadMethods(UserMethods):
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Private methods
|
|
||||||
|
|
||||||
def _get_response_message(self, request, result, input_chat):
|
|
||||||
"""
|
|
||||||
Extracts the response message known a request and Update result.
|
|
||||||
The request may also be the ID of the message to match.
|
|
||||||
"""
|
|
||||||
# Telegram seems to send updateMessageID first, then updateNewMessage,
|
|
||||||
# however let's not rely on that just in case.
|
|
||||||
if isinstance(request, int):
|
|
||||||
msg_id = request
|
|
||||||
else:
|
|
||||||
msg_id = None
|
|
||||||
for update in result.updates:
|
|
||||||
if isinstance(update, types.UpdateMessageID):
|
|
||||||
if update.random_id == request.random_id:
|
|
||||||
msg_id = update.id
|
|
||||||
break
|
|
||||||
|
|
||||||
if isinstance(result, types.UpdateShort):
|
|
||||||
updates = [result.update]
|
|
||||||
entities = {}
|
|
||||||
elif isinstance(result, (types.Updates, types.UpdatesCombined)):
|
|
||||||
updates = result.updates
|
|
||||||
entities = {utils.get_peer_id(x): x
|
|
||||||
for x in
|
|
||||||
itertools.chain(result.users, result.chats)}
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
found = None
|
|
||||||
for update in updates:
|
|
||||||
if isinstance(update, (
|
|
||||||
types.UpdateNewChannelMessage,
|
|
||||||
types.UpdateNewMessage)):
|
|
||||||
if update.message.id == msg_id:
|
|
||||||
found = update.message
|
|
||||||
break
|
|
||||||
|
|
||||||
elif (isinstance(update, types.UpdateEditMessage) and
|
|
||||||
not isinstance(request.peer,
|
|
||||||
types.InputPeerChannel)):
|
|
||||||
if request.id == update.message.id:
|
|
||||||
found = update.message
|
|
||||||
break
|
|
||||||
|
|
||||||
elif (isinstance(update, types.UpdateEditChannelMessage) and
|
|
||||||
utils.get_peer_id(request.peer) ==
|
|
||||||
utils.get_peer_id(update.message.to_id)):
|
|
||||||
if request.id == update.message.id:
|
|
||||||
found = update.message
|
|
||||||
break
|
|
||||||
|
|
||||||
if found:
|
|
||||||
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))
|
|
||||||
|
|
||||||
async def _parse_message_text(self, message, parse_mode):
|
|
||||||
"""
|
|
||||||
Returns a (parsed message, entities) tuple depending on ``parse_mode``.
|
|
||||||
"""
|
|
||||||
if parse_mode == utils.Default:
|
|
||||||
parse_mode = self._parse_mode
|
|
||||||
else:
|
|
||||||
parse_mode = self._sanitize_parse_mode(parse_mode)
|
|
||||||
|
|
||||||
if not parse_mode:
|
|
||||||
return message, []
|
|
||||||
|
|
||||||
message, msg_entities = parse_mode.parse(message)
|
|
||||||
for i, e in enumerate(msg_entities):
|
|
||||||
if isinstance(e, types.MessageEntityTextUrl):
|
|
||||||
m = re.match(r'^@|\+|tg://user\?id=(\d+)', e.url)
|
|
||||||
if m:
|
|
||||||
try:
|
|
||||||
msg_entities[i] = types.InputMessageEntityMentionName(
|
|
||||||
e.offset, e.length, await self.get_input_entity(
|
|
||||||
int(m.group(1)) if m.group(1) else e.url
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
# Make no replacement
|
|
||||||
pass
|
|
||||||
|
|
||||||
return message, msg_entities
|
|
||||||
|
|
||||||
async def _file_to_media(
|
async def _file_to_media(
|
||||||
self, file, force_document=False,
|
self, file, force_document=False,
|
||||||
progress_callback=None, attributes=None, thumb=None,
|
progress_callback=None, attributes=None, thumb=None,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import types
|
||||||
from collections import UserList
|
from collections import UserList
|
||||||
from mimetypes import guess_extension
|
from mimetypes import guess_extension
|
||||||
|
|
||||||
|
from .extensions import markdown, html
|
||||||
from .tl import TLObject
|
from .tl import TLObject
|
||||||
from .tl.types import (
|
from .tl.types import (
|
||||||
Channel, ChannelForbidden, Chat, ChatEmpty, ChatForbidden, ChatFull,
|
Channel, ChannelForbidden, Chat, ChatEmpty, ChatForbidden, ChatFull,
|
||||||
|
@ -423,6 +424,39 @@ def get_message_id(message):
|
||||||
raise TypeError('Invalid message type: {}'.format(type(message)))
|
raise TypeError('Invalid message type: {}'.format(type(message)))
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_parse_mode(mode):
|
||||||
|
"""
|
||||||
|
Converts the given parse mode into an object with
|
||||||
|
``parse`` and ``unparse`` callable properties.
|
||||||
|
"""
|
||||||
|
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 get_input_location(location):
|
def get_input_location(location):
|
||||||
"""Similar to :meth:`get_input_peer`, but for input messages."""
|
"""Similar to :meth:`get_input_peer`, but for input messages."""
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user