2018-04-05 21:14:22 +03:00
|
|
|
from .common import EventBuilder, EventCommon, name_inner_event
|
|
|
|
from .. import utils
|
2018-09-09 16:48:54 +03:00
|
|
|
from ..tl import types, functions
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
|
|
|
|
@name_inner_event
|
|
|
|
class ChatAction(EventBuilder):
|
|
|
|
"""
|
|
|
|
Represents an action in a chat (such as user joined, left, or new pin).
|
|
|
|
"""
|
2018-07-19 02:47:32 +03:00
|
|
|
@classmethod
|
|
|
|
def build(cls, update):
|
2018-04-05 21:14:22 +03:00
|
|
|
if isinstance(update, types.UpdateChannelPinnedMessage) and update.id == 0:
|
|
|
|
# Telegram does not always send
|
|
|
|
# UpdateChannelPinnedMessage for new pins
|
|
|
|
# but always for unpin, with update.id = 0
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(types.PeerChannel(update.channel_id),
|
|
|
|
unpin=True)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
elif isinstance(update, types.UpdateChatParticipantAdd):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(types.PeerChat(update.chat_id),
|
|
|
|
added_by=update.inviter_id or True,
|
|
|
|
users=update.user_id)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
elif isinstance(update, types.UpdateChatParticipantDelete):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(types.PeerChat(update.chat_id),
|
|
|
|
kicked_by=True,
|
|
|
|
users=update.user_id)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
elif (isinstance(update, (
|
|
|
|
types.UpdateNewMessage, types.UpdateNewChannelMessage))
|
|
|
|
and isinstance(update.message, types.MessageService)):
|
|
|
|
msg = update.message
|
|
|
|
action = update.message.action
|
|
|
|
if isinstance(action, types.MessageActionChatJoinedByLink):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
added_by=True,
|
|
|
|
users=msg.from_id)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionChatAddUser):
|
2018-09-22 20:18:42 +03:00
|
|
|
# If a user adds itself, it means they joined
|
2018-06-25 14:21:23 +03:00
|
|
|
added_by = ([msg.from_id] == action.users) or msg.from_id
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
added_by=added_by,
|
|
|
|
users=action.users)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionChatDeleteUser):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
kicked_by=msg.from_id or True,
|
|
|
|
users=action.user_id)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionChatCreate):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
users=action.users,
|
|
|
|
created=True,
|
|
|
|
new_title=action.title)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionChannelCreate):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
created=True,
|
|
|
|
users=msg.from_id,
|
|
|
|
new_title=action.title)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionChatEditTitle):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
users=msg.from_id,
|
|
|
|
new_title=action.title)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionChatEditPhoto):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
users=msg.from_id,
|
|
|
|
new_photo=action.photo)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionChatDeletePhoto):
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
users=msg.from_id,
|
|
|
|
new_photo=True)
|
2018-04-05 21:14:22 +03:00
|
|
|
elif isinstance(action, types.MessageActionPinMessage):
|
|
|
|
# Telegram always sends this service message for new pins
|
2018-07-19 02:47:32 +03:00
|
|
|
event = cls.Event(msg,
|
|
|
|
users=msg.from_id,
|
|
|
|
new_pin=msg.reply_to_msg_id)
|
2018-04-05 21:14:22 +03:00
|
|
|
else:
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
event._entities = update._entities
|
2018-07-11 12:22:43 +03:00
|
|
|
return event
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
class Event(EventCommon):
|
|
|
|
"""
|
|
|
|
Represents the event of a new chat action.
|
|
|
|
|
|
|
|
Members:
|
2018-04-08 15:15:26 +03:00
|
|
|
action_message (`MessageAction <https://lonamiwebs.github.io/Telethon/types/message_action.html>`_):
|
|
|
|
The message invoked by this Chat Action.
|
|
|
|
|
2018-04-05 21:14:22 +03:00
|
|
|
new_pin (`bool`):
|
|
|
|
``True`` if there is a new pin.
|
|
|
|
|
|
|
|
new_photo (`bool`):
|
|
|
|
``True`` if there's a new chat photo (or it was removed).
|
|
|
|
|
|
|
|
photo (:tl:`Photo`, optional):
|
|
|
|
The new photo (or ``None`` if it was removed).
|
|
|
|
|
|
|
|
user_added (`bool`):
|
|
|
|
``True`` if the user was added by some other.
|
|
|
|
|
|
|
|
user_joined (`bool`):
|
|
|
|
``True`` if the user joined on their own.
|
|
|
|
|
|
|
|
user_left (`bool`):
|
|
|
|
``True`` if the user left on their own.
|
|
|
|
|
|
|
|
user_kicked (`bool`):
|
|
|
|
``True`` if the user was kicked by some other.
|
|
|
|
|
|
|
|
created (`bool`, optional):
|
|
|
|
``True`` if this chat was just created.
|
|
|
|
|
2018-04-28 14:42:36 +03:00
|
|
|
new_title (`str`, optional):
|
2018-04-05 21:14:22 +03:00
|
|
|
The new title string for the chat, if applicable.
|
|
|
|
|
|
|
|
unpin (`bool`):
|
|
|
|
``True`` if the existing pin gets unpinned.
|
|
|
|
"""
|
|
|
|
def __init__(self, where, new_pin=None, new_photo=None,
|
|
|
|
added_by=None, kicked_by=None, created=None,
|
|
|
|
users=None, new_title=None, unpin=None):
|
|
|
|
if isinstance(where, types.MessageService):
|
|
|
|
self.action_message = where
|
|
|
|
where = where.to_id
|
|
|
|
else:
|
|
|
|
self.action_message = None
|
|
|
|
|
|
|
|
super().__init__(chat_peer=where, msg_id=new_pin)
|
|
|
|
|
|
|
|
self.new_pin = isinstance(new_pin, int)
|
|
|
|
self._pinned_message = new_pin
|
|
|
|
|
|
|
|
self.new_photo = new_photo is not None
|
|
|
|
self.photo = \
|
|
|
|
new_photo if isinstance(new_photo, types.Photo) else None
|
|
|
|
|
|
|
|
self._added_by = None
|
|
|
|
self._kicked_by = None
|
2018-09-09 16:48:54 +03:00
|
|
|
self.user_added = self.user_joined = self.user_left = \
|
|
|
|
self.user_kicked = self.unpin = False
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
if added_by is True:
|
|
|
|
self.user_joined = True
|
|
|
|
elif added_by:
|
|
|
|
self.user_added = True
|
|
|
|
self._added_by = added_by
|
|
|
|
|
|
|
|
if kicked_by is True:
|
|
|
|
self.user_left = True
|
|
|
|
elif kicked_by:
|
|
|
|
self.user_kicked = True
|
|
|
|
self._kicked_by = kicked_by
|
|
|
|
|
|
|
|
self.created = bool(created)
|
|
|
|
self._user_peers = users if isinstance(users, list) else [users]
|
|
|
|
self._users = None
|
|
|
|
self._input_users = None
|
|
|
|
self.new_title = new_title
|
|
|
|
self.unpin = unpin
|
|
|
|
|
2018-05-31 14:30:22 +03:00
|
|
|
def _set_client(self, client):
|
|
|
|
super()._set_client(client)
|
|
|
|
if self.action_message:
|
2018-07-22 20:20:55 +03:00
|
|
|
self.action_message._finish_init(client, self._entities, None)
|
2018-05-31 14:30:22 +03:00
|
|
|
|
2018-06-14 18:09:20 +03:00
|
|
|
async def respond(self, *args, **kwargs):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
2018-05-17 13:00:22 +03:00
|
|
|
Responds to the chat action message (not as a reply). Shorthand for
|
2018-10-17 12:47:51 +03:00
|
|
|
`telethon.client.messages.MessageMethods.send_message` with
|
2018-05-17 13:00:22 +03:00
|
|
|
``entity`` already set.
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
2018-06-14 18:09:20 +03:00
|
|
|
return await self._client.send_message(
|
2018-06-25 12:38:56 +03:00
|
|
|
await self.get_input_chat(), *args, **kwargs)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
2018-06-14 18:09:20 +03:00
|
|
|
async def reply(self, *args, **kwargs):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
Replies to the chat action message (as a reply). Shorthand for
|
2018-10-17 12:47:51 +03:00
|
|
|
`telethon.client.messages.MessageMethods.send_message` with
|
2018-05-17 13:00:22 +03:00
|
|
|
both ``entity`` and ``reply_to`` already set.
|
2018-04-05 21:14:22 +03:00
|
|
|
|
2018-05-17 13:00:22 +03:00
|
|
|
Has the same effect as `respond` if there is no message.
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
if not self.action_message:
|
2018-06-14 18:09:20 +03:00
|
|
|
return await self.respond(*args, **kwargs)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
kwargs['reply_to'] = self.action_message.id
|
2018-06-14 18:09:20 +03:00
|
|
|
return await self._client.send_message(
|
2018-06-25 12:38:56 +03:00
|
|
|
await self.get_input_chat(), *args, **kwargs)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
2018-06-14 18:09:20 +03:00
|
|
|
async def delete(self, *args, **kwargs):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
Deletes the chat action message. You're responsible for checking
|
|
|
|
whether you have the permission to do so, or to except the error
|
2018-05-17 13:00:22 +03:00
|
|
|
otherwise. Shorthand for
|
2018-10-17 12:47:51 +03:00
|
|
|
`telethon.client.messages.MessageMethods.delete_messages` with
|
2018-05-17 13:00:22 +03:00
|
|
|
``entity`` and ``message_ids`` already set.
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
Does nothing if no message action triggered this event.
|
|
|
|
"""
|
2018-06-14 18:09:20 +03:00
|
|
|
if not self.action_message:
|
|
|
|
return
|
|
|
|
|
|
|
|
return await self._client.delete_messages(
|
2018-06-25 12:38:56 +03:00
|
|
|
await self.get_input_chat(), [self.action_message],
|
|
|
|
*args, **kwargs
|
|
|
|
)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
2018-06-25 12:03:20 +03:00
|
|
|
async def get_pinned_message(self):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
2018-06-02 13:52:38 +03:00
|
|
|
If ``new_pin`` is ``True``, this returns the
|
|
|
|
`telethon.tl.custom.message.Message` object that was pinned.
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
if self._pinned_message == 0:
|
|
|
|
return None
|
|
|
|
|
2018-06-25 12:38:56 +03:00
|
|
|
if isinstance(self._pinned_message, int)\
|
|
|
|
and await self.get_input_chat():
|
2018-06-14 18:09:20 +03:00
|
|
|
r = await self._client(functions.channels.GetMessagesRequest(
|
2018-04-23 12:05:38 +03:00
|
|
|
self._input_chat, [self._pinned_message]
|
2018-04-05 21:14:22 +03:00
|
|
|
))
|
|
|
|
try:
|
|
|
|
self._pinned_message = next(
|
|
|
|
x for x in r.messages
|
|
|
|
if isinstance(x, types.Message)
|
|
|
|
and x.id == self._pinned_message
|
|
|
|
)
|
|
|
|
except StopIteration:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if isinstance(self._pinned_message, types.Message):
|
|
|
|
return self._pinned_message
|
|
|
|
|
|
|
|
@property
|
2018-06-25 12:03:20 +03:00
|
|
|
def added_by(self):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
The user who added ``users``, if applicable (``None`` otherwise).
|
|
|
|
"""
|
|
|
|
if self._added_by and not isinstance(self._added_by, types.User):
|
2018-06-14 18:09:20 +03:00
|
|
|
aby = self._entities.get(utils.get_peer_id(self._added_by))
|
2018-06-25 12:03:20 +03:00
|
|
|
if aby:
|
|
|
|
self._added_by = aby
|
|
|
|
|
|
|
|
return self._added_by
|
|
|
|
|
|
|
|
async def get_added_by(self):
|
|
|
|
"""
|
|
|
|
Returns `added_by` but will make an API call if necessary.
|
|
|
|
"""
|
|
|
|
if not self.added_by and self._added_by:
|
|
|
|
self._added_by = await self._client.get_entity(self._added_by)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
return self._added_by
|
|
|
|
|
|
|
|
@property
|
2018-06-25 12:03:20 +03:00
|
|
|
def kicked_by(self):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
The user who kicked ``users``, if applicable (``None`` otherwise).
|
|
|
|
"""
|
|
|
|
if self._kicked_by and not isinstance(self._kicked_by, types.User):
|
2018-06-14 18:09:20 +03:00
|
|
|
kby = self._entities.get(utils.get_peer_id(self._kicked_by))
|
|
|
|
if kby:
|
2018-06-25 12:03:20 +03:00
|
|
|
self._kicked_by = kby
|
|
|
|
|
|
|
|
return self._kicked_by
|
|
|
|
|
|
|
|
async def get_kicked_by(self):
|
|
|
|
"""
|
|
|
|
Returns `kicked_by` but will make an API call if necessary.
|
|
|
|
"""
|
|
|
|
if not self.kicked_by and self._kicked_by:
|
|
|
|
self._kicked_by = await self._client.get_entity(self._kicked_by)
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
return self._kicked_by
|
|
|
|
|
|
|
|
@property
|
2018-06-25 12:03:20 +03:00
|
|
|
def user(self):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
2018-04-08 15:22:11 +03:00
|
|
|
The first user that takes part in this action (e.g. joined).
|
2018-04-05 21:14:22 +03:00
|
|
|
|
|
|
|
Might be ``None`` if the information can't be retrieved or
|
|
|
|
there is no user taking part.
|
|
|
|
"""
|
2018-06-25 12:03:20 +03:00
|
|
|
if self.users:
|
2018-04-05 21:14:22 +03:00
|
|
|
return self._users[0]
|
|
|
|
|
2018-06-25 12:03:20 +03:00
|
|
|
async def get_user(self):
|
|
|
|
"""
|
|
|
|
Returns `user` but will make an API call if necessary.
|
|
|
|
"""
|
|
|
|
if self.users or await self.get_users():
|
|
|
|
return self._users[0]
|
|
|
|
|
|
|
|
def input_user(self):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
Input version of the ``self.user`` property.
|
|
|
|
"""
|
2018-06-25 12:03:20 +03:00
|
|
|
if self.input_users:
|
|
|
|
return self._input_users[0]
|
|
|
|
|
|
|
|
async def get_input_user(self):
|
|
|
|
"""
|
|
|
|
Returns `input_user` but will make an API call if necessary.
|
|
|
|
"""
|
|
|
|
if self.input_users or await self.get_input_users():
|
2018-04-05 21:14:22 +03:00
|
|
|
return self._input_users[0]
|
|
|
|
|
2018-04-08 15:22:11 +03:00
|
|
|
@property
|
|
|
|
def user_id(self):
|
|
|
|
"""
|
|
|
|
Returns the marked signed ID of the first user, if any.
|
|
|
|
"""
|
2018-04-08 16:55:10 +03:00
|
|
|
if self._user_peers:
|
|
|
|
return utils.get_peer_id(self._user_peers[0])
|
2018-04-08 15:22:11 +03:00
|
|
|
|
2018-04-05 21:14:22 +03:00
|
|
|
@property
|
2018-06-25 12:03:20 +03:00
|
|
|
def users(self):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
A list of users that take part in this action (e.g. joined).
|
|
|
|
|
|
|
|
Might be empty if the information can't be retrieved or there
|
|
|
|
are no users taking part.
|
|
|
|
"""
|
|
|
|
if not self._user_peers:
|
|
|
|
return []
|
|
|
|
|
|
|
|
if self._users is None:
|
2018-06-25 12:03:20 +03:00
|
|
|
self._users = [
|
|
|
|
self._entities[utils.get_peer_id(peer)]
|
|
|
|
for peer in self._user_peers
|
|
|
|
if utils.get_peer_id(peer) in self._entities
|
|
|
|
]
|
|
|
|
|
|
|
|
return self._users
|
|
|
|
|
|
|
|
async def get_users(self):
|
|
|
|
"""
|
|
|
|
Returns `users` but will make an API call if necessary.
|
|
|
|
"""
|
|
|
|
if not self._user_peers:
|
|
|
|
return []
|
|
|
|
|
|
|
|
if self._users is None or len(self._users) != len(self._user_peers):
|
2018-04-05 21:14:22 +03:00
|
|
|
have, missing = [], []
|
|
|
|
for peer in self._user_peers:
|
|
|
|
user = self._entities.get(utils.get_peer_id(peer))
|
|
|
|
if user:
|
|
|
|
have.append(user)
|
|
|
|
else:
|
|
|
|
missing.append(peer)
|
|
|
|
|
|
|
|
try:
|
2018-06-14 18:09:20 +03:00
|
|
|
missing = await self._client.get_entity(missing)
|
2018-04-05 21:14:22 +03:00
|
|
|
except (TypeError, ValueError):
|
|
|
|
missing = []
|
|
|
|
|
|
|
|
self._users = have + missing
|
|
|
|
|
|
|
|
return self._users
|
|
|
|
|
|
|
|
@property
|
2018-06-25 12:03:20 +03:00
|
|
|
def input_users(self):
|
2018-04-05 21:14:22 +03:00
|
|
|
"""
|
|
|
|
Input version of the ``self.users`` property.
|
|
|
|
"""
|
|
|
|
if self._input_users is None and self._user_peers:
|
|
|
|
self._input_users = []
|
|
|
|
for peer in self._user_peers:
|
|
|
|
try:
|
2018-06-14 18:09:20 +03:00
|
|
|
self._input_users.append(
|
2018-06-25 12:03:20 +03:00
|
|
|
self._client.session.get_input_entity(peer)
|
2018-06-14 18:09:20 +03:00
|
|
|
)
|
2018-06-25 12:03:20 +03:00
|
|
|
except ValueError:
|
2018-04-05 21:14:22 +03:00
|
|
|
pass
|
2018-06-25 12:03:20 +03:00
|
|
|
return self._input_users or []
|
|
|
|
|
|
|
|
async def get_input_users(self):
|
|
|
|
"""
|
|
|
|
Returns `input_users` but will make an API call if necessary.
|
|
|
|
"""
|
|
|
|
# TODO Maybe we could re-fetch the message
|
|
|
|
return self.input_users
|
2018-04-08 15:22:11 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def user_ids(self):
|
|
|
|
"""
|
|
|
|
Returns the marked signed ID of the users, if any.
|
|
|
|
"""
|
2018-04-08 16:55:10 +03:00
|
|
|
if self._user_peers:
|
|
|
|
return [utils.get_peer_id(u) for u in self._user_peers]
|