Add a NewMessage event to handle incoming messages

This commit is contained in:
Lonami Exo 2018-02-07 10:42:40 +01:00
parent 5ec984dd82
commit ef837b1a53

View File

@ -12,3 +12,243 @@ class _EventBuilder(abc.ABC):
@abc.abstractmethod
def resolve(self, client):
"""Helper method to allow event builders to be resolved before usage"""
# Classes defined here are actually Event builders
# for their inner Event classes. Inner ._client is
# set later by the creator TelegramClient.
class NewMessage(_EventBuilder):
"""
Represents a new message event builder.
Args:
incoming (:obj:`bool`, optional):
If set to ``True``, only **incoming** messages will be handled.
Mutually exclusive with ``outgoing`` (can only set one of either).
outgoing (:obj:`bool`, optional):
If set to ``True``, only **outgoing** messages will be handled.
Mutually exclusive with ``incoming`` (can only set one of either).
chats (:obj:`entity`, optional):
May be one or more entities (username/peer/etc.). By default,
only matching chats will be handled.
blacklist_chats (:obj:`bool`, optional):
Whether to treat the the list of chats as a blacklist (if
it matches it will NOT be handled) or a whitelist (default).
"""
def __init__(self, incoming=None, outgoing=None,
chats=None, blacklist_chats=False,
require_input=True):
if incoming and outgoing:
raise ValueError('Can only set either incoming or outgoing')
self.incoming = incoming
self.outgoing = outgoing
self.chats = chats
self.blacklist_chats = blacklist_chats
def resolve(self, client):
if hasattr(self.chats, '__iter__') and not isinstance(self.chats, str):
self.chats = set(utils.get_peer_id(x)
for x in client.get_input_entity(self.chats))
elif self.chats is not None:
self.chats = {utils.get_peer_id(
client.get_input_entity(self.chats))}
def build(self, update):
if isinstance(update,
(types.UpdateNewMessage, types.UpdateNewChannelMessage)):
event = NewMessage.Event(update.message)
elif isinstance(update, types.UpdateShortMessage):
event = NewMessage.Event(types.Message(
out=update.out,
mentioned=update.mentioned,
media_unread=update.media_unread,
silent=update.silent,
id=update.id,
to_id=types.PeerUser(update.user_id),
message=update.message,
date=update.date,
fwd_from=update.fwd_from,
via_bot_id=update.via_bot_id,
reply_to_msg_id=update.reply_to_msg_id,
entities=update.entities
))
else:
return
# Short-circuit if we let pass all events
if all(x is None for x in (self.incoming, self.outgoing, self.chats)):
return event
if self.incoming and event.message.out:
return
if self.outgoing and not event.message.out:
return
if self.chats is not None:
inside = utils.get_peer_id(event.input_chat) in self.chats
if inside == self.blacklist_chats:
# If this chat matches but it's a blacklist ignore.
# If it doesn't match but it's a whitelist ignore.
return
# Tests passed so return the event
return event
class Event:
"""
Represents the event of a new message.
Members:
message (:obj:`Message`):
This is the original ``Message`` object.
input_chat (:obj:`InputPeer`):
This is the input chat (private, group, megagroup or channel)
to which the message was sent. This doesn't have the title or
anything, but is useful if you don't need those to avoid
further requests.
Note that this might not be available if the library can't
find the input chat.
chat (:obj:`User` | :obj:`Chat` | :obj:`Channel`, optional):
This property will make an API call the first time to get the
most up to date version of the chat, so use with care as
there is no caching besides local caching yet.
``input_chat`` needs to be available (often the case).
is_private (:obj:`bool`):
True if the message was sent as a private message.
is_group (:obj:`bool`):
True if the message was sent on a group or megagroup.
is_channel (:obj:`bool`):
True if the message was sent on a megagroup or channel.
input_sender (:obj:`InputPeer`):
This is the input version of the user who sent the message.
Similarly to ``input_chat``, this doesn't have things like
username or similar, but still useful in some cases.
Note that this might not be available if the library can't
find the input chat.
sender (:obj:`User`):
This property will make an API call the first time to get the
most up to date version of the sender, so use with care as
there is no caching besides local caching yet.
``input_sender`` needs to be available (often the case).
text (:obj:`str`):
The message text, markdown-formatted.
raw_text (:obj:`str`):
The raw message text, ignoring any formatting.
is_reply (:obj:`str`):
Whether the message is a reply to some other or not.
reply_message (:obj:`Message`, optional):
This property will make an API call the first time to get the
full ``Message`` object that one was replying to, so use with
care as there is no caching besides local caching yet.
forward (:obj:`MessageFwdHeader`, optional):
The unmodified ``MessageFwdHeader``, if present.
out (:obj:`bool`):
Whether the message is outgoing (i.e. you sent it from
another session) or incoming (i.e. someone else sent it).
"""
def __init__(self, message):
self._client = None
self.message = message
self._text = None
self._chat = None
self._sender = None
self.is_private = isinstance(message.to_id, types.PeerUser)
self.is_group = (
isinstance(message.to_id, (types.PeerChat, types.PeerChannel))
and not message.post
)
self.is_channel = isinstance(message.to_id, types.PeerChannel)
self.is_reply = bool(message.reply_to_msg_id)
self._reply_message = None
def reply(self, message, as_reply=True):
"""Replies to this message"""
self._client.send_message(self.message.to_id, message)
@property
def input_chat(self):
# TODO If not found, getMessages to find the sender and chat
return self._client.get_input_entity(self.message.to_id)
@property
def chat(self):
if self._chat is None:
# TODO Assert input entity is not None to avoid weird errors
self._chat = self._client.get_entity(self.input_chat)
return self._chat
@property
def input_sender(self):
# TODO If not found, getMessages to find the sender and chat
return self._client.get_input_entity(self.message.from_id)
@property
def sender(self):
if self._sender is None:
# TODO Assert input entity is not None to avoid weird errors
self._sender = self._client.get_entity(self.input_sender)
return self._sender
@property
def text(self):
if self._text is None:
if not self.message.entities:
return self.message.message
self._text = markdown.unparse(self.message.message,
self.message.entities or [])
return self._text
@property
def raw_text(self):
return self.message.message
@property
def reply_message(self):
if not self.message.reply_to_msg_id:
return None
if self._reply_message is None:
if isinstance(self.input_chat, types.InputPeerChannel):
r = self._client(functions.channels.GetMessagesRequest(
self.input_chat, [self.message.reply_to_msg_id]
))
else:
r = self._client(functions.messages.GetMessagesRequest(
[self.message.reply_to_msg_id]
))
if not isinstance(r, types.messages.MessagesNotModified):
self._reply_message = r.messages[0]
return self._reply_message
@property
def forward(self):
return self.message.fwd_from
@property
def out(self):
return self.message.out