[ADD] Full support of groups service messages

This commit is contained in:
annelin
2018-06-19 07:24:33 +00:00
parent bca6860159
commit 55f85db5d1

View File

@@ -9,7 +9,7 @@ from telethon.tl.types import MessageMediaDocument, MessageMediaPhoto, MessageMe
MessageMediaGeo, MessageMediaEmpty, MessageMediaVenue MessageMediaGeo, MessageMediaEmpty, MessageMediaVenue
from telethon.tl.types import DocumentAttributeAnimated, DocumentAttributeAudio, DocumentAttributeFilename,\ from telethon.tl.types import DocumentAttributeAnimated, DocumentAttributeAudio, DocumentAttributeFilename,\
DocumentAttributeSticker, DocumentAttributeVideo, DocumentAttributeHasStickers DocumentAttributeSticker, DocumentAttributeVideo, DocumentAttributeHasStickers
from telethon.tl.types import MessageService, MessageActionChannelCreate, MessageActionChannelMigrateFrom,\ from telethon.tl.types import Message, MessageService, MessageActionChannelCreate, MessageActionChannelMigrateFrom,\
MessageActionChatCreate, MessageActionChatAddUser, MessageActionChatDeleteUser,\ MessageActionChatCreate, MessageActionChatAddUser, MessageActionChatDeleteUser,\
MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo,\ MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo,\
MessageActionPinMessage MessageActionPinMessage
@@ -17,8 +17,8 @@ from telethon.tl.types import UserStatusOnline, UserStatusOffline, UserStatusRec
from telethon.tl.types import User, Chat, Channel from telethon.tl.types import User, Chat, Channel
from telethon.tl.types import PeerUser, PeerChat, PeerChannel from telethon.tl.types import PeerUser, PeerChat, PeerChannel
from telethon.tl.functions.users import GetFullUserRequest from telethon.tl.functions.users import GetFullUserRequest
from telethon.tl.functions.messages import ReadHistoryRequest, GetFullChatRequest from telethon.tl.functions.messages import ReadHistoryRequest, GetFullChatRequest, GetMessagesRequest
from telethon.tl.functions.channels import ReadHistoryRequest as ReadHistoryChannel, GetParticipantRequest from telethon.tl.functions.channels import ReadHistoryRequest as ReadHistoryChannel, GetParticipantRequest, GetMessagesRequest
from telethon.tl.functions.updates import GetDifferenceRequest from telethon.tl.functions.updates import GetDifferenceRequest
from telethon.tl.functions.contacts import ResolveUsernameRequest from telethon.tl.functions.contacts import ResolveUsernameRequest
@@ -62,8 +62,10 @@ class TelegramGateClient(TelegramClient):
print('new update for ' + self.jid) print('new update for ' + self.jid)
print(type(obj), obj.__dict__) print(type(obj), obj.__dict__)
# link to self-user #
if not self.me: if not self.me:
self.me = self.get_me() me = self.get_me()
self.me = InputPeerUser(me.id, me.access_hash)
''' '''
Боты Боты
@@ -87,10 +89,8 @@ class TelegramGateClient(TelegramClient):
fwd_from = self._process_forward_msg(obj) if obj.fwd_from else '' # process forward messages fwd_from = self._process_forward_msg(obj) if obj.fwd_from else '' # process forward messages
self.gate_send_message( mfrom='u' + str(obj.user_id), mbody = '[MSG {}] {}{}'.format(obj.id, fwd_from, obj.message) ) self.gate_send_message( mfrom='u' + str(obj.user_id), mbody = '[MSG {}] {}{}'.format(obj.id, fwd_from, obj.message) )
usr = self._get_user_information(obj.user_id) # get peer information
if obj.user_id in self.xmpp_gate.tg_dialogs[self.jid]['users']: # make as read self.invoke(ReadHistoryRequest( InputPeerUser(usr.id, usr.access_hash), obj.id )) # delivery report
usr = self.xmpp_gate.tg_dialogs[self.jid]['users'][obj.user_id]
self.invoke(ReadHistoryRequest( InputPeerUser(usr.id, usr.access_hash), obj.id ))
# message from normal group # # message from normal group #
if type(obj) in [UpdateShortChatMessage] and not obj.out: if type(obj) in [UpdateShortChatMessage] and not obj.out:
@@ -115,44 +115,53 @@ class TelegramGateClient(TelegramClient):
if type(obj) in [UpdateNewMessage, UpdateNewChannelMessage, UpdateEditMessage, UpdateEditChannelMessage] and not obj.message.out: if type(obj) in [UpdateNewMessage, UpdateNewChannelMessage, UpdateEditMessage, UpdateEditChannelMessage] and not obj.message.out:
cid = None cid = None
mid = obj.message.id msg = ''
msg = obj.message.message
fwd_from = '' fwd_from = ''
mid = obj.message.id
# detect message type # detect message type
is_user = type(obj.message.to_id) is PeerUser is_user = type(obj.message.to_id) is PeerUser
is_group = type(obj.message.to_id) is PeerChat is_group = type(obj.message.to_id) is PeerChat
is_supergroup = type(obj.message.to_id) is PeerChannel is_supergroup = type(obj.message.to_id) is PeerChannel
# is forwarded?
if obj.message.fwd_from:
fwd_from = self._process_forward_msg(obj.message)
# detect from id # detect from id
if is_user: if is_user:
cid = obj.message.from_id cid = obj.message.from_id
usr = self.xmpp_gate.tg_dialogs[self.jid]['users'][cid] peer = InputPeerUser(cid, self.xmpp_gate.tg_dialogs[self.jid]['users'][cid].access_hash) if cid in self.xmpp_gate.tg_dialogs[self.jid]['users'] else None
prefix = 'u' prefix = 'u'
elif is_group: elif is_group:
cid = obj.message.to_id.chat_id cid = obj.message.to_id.chat_id
peer = InputPeerChat(cid)
prefix = 'g' prefix = 'g'
elif is_supergroup: elif is_supergroup:
cid = obj.message.to_id.channel_id cid = obj.message.to_id.channel_id
access_hash = self.xmpp_gate.tg_dialogs[self.jid]['supergroups'][cid].access_hash if cid in self.xmpp_gate.tg_dialogs[self.jid]['supergroups'] else None peer = InputPeerChannel(cid, self.xmpp_gate.tg_dialogs[self.jid]['supergroups'][cid].access_hash) if cid in self.xmpp_gate.tg_dialogs[self.jid]['supergroups'] else None
prefix = 's' prefix = 's'
# our message #
if type(obj.message) == MessageService:
obj.message.fwd_from, obj.message.post, obj.message.edit_date, obj.message.media = None, None, None, None
msg = self._process_info_msg(obj.message, peer)
elif type(obj.message) == Message:
msg = obj.message.message
# is forwarded?
if obj.message.fwd_from:
fwd_from = self._process_forward_msg(obj.message)
# maybe its channel? # # maybe its channel? #
if obj.message.post: if obj.message.post:
prefix = 'c' prefix = 'c'
# maybe its forwarded? #
# get sender information from chat info # # get sender information from chat info #
if not is_user and not obj.message.post: if not is_user and not obj.message.post:
if obj.message.from_id not in self._groups_users: if obj.message.from_id not in self._groups_users:
chat_info = self.invoke(GetFullChatRequest(cid)) if is_group else self.invoke(GetParticipantRequest(InputPeerChannel(cid, access_hash), InputPeerUser(self.me.id, self.me.access_hash))) chat = self.invoke(GetFullChatRequest(cid)) if is_group else self.invoke(GetParticipantRequest(peer, self.me))
for usr in chat_info.users: for usr in chat.users:
self._groups_users[usr.id] = usr self._groups_users[usr.id] = usr
nickname = display_tg_name(self._groups_users[obj.message.from_id].first_name, self._groups_users[obj.message.from_id].last_name) nickname = display_tg_name(self._groups_users[obj.message.from_id].first_name, self._groups_users[obj.message.from_id].last_name)
@@ -171,20 +180,14 @@ class TelegramGateClient(TelegramClient):
self.gate_send_message(prefix + str(cid), mbody = '[MSG {}] {}{}'.format(mid, fwd_from, msg) ) self.gate_send_message(prefix + str(cid), mbody = '[MSG {}] {}{}'.format(mid, fwd_from, msg) )
# delivery report # delivery report
if is_user and usr.access_hash: # make as read if is_supergroup:
self.invoke(ReadHistoryRequest( InputPeerUser(usr.id, usr.access_hash), mid )) self.invoke(ReadHistoryChannel(peer, mid))
if is_group: else:
self.invoke(ReadHistoryRequest(InputPeerChat(cid), mid)) self.invoke(ReadHistoryRequest(peer, mid))
if is_supergroup and access_hash:
self.invoke(ReadHistoryChannel(InputPeerChannel(cid, access_hash), mid))
# Status Updates # # Status Updates #
if type(obj) is UpdateUserStatus: if type(obj) is UpdateUserStatus:
print(self._status_last)
print(time.time())
# save last update time # # save last update time #
if (obj.user_id in self._status_last) and ( (time.time() - self._status_last[obj.user_id]['time'] < self.user_options['status_update_interval']) or self._status_last[obj.user_id]['status'] == obj.status ): if (obj.user_id in self._status_last) and ( (time.time() - self._status_last[obj.user_id]['time'] < self.user_options['status_update_interval']) or self._status_last[obj.user_id]['status'] == obj.status ):
return return
@@ -256,6 +259,19 @@ class TelegramGateClient(TelegramClient):
return attrib return attrib
return None return None
def _get_user_information(self, uid):
if uid in self.xmpp_gate.tg_dialogs[self.jid]['users']:
return self.xmpp_gate.tg_dialogs[self.jid]['users'][uid]
entity = self.get_entity(uid)
if entity.access_hash:
self.xmpp_gate.tg_dialogs[self.jid]['users'][uid] = entity
return entity
else:
return {'first_name': 'Unknown', 'last_name': 'user', 'access_hash': -1, 'id': 0}
def _process_forward_msg(self, message): def _process_forward_msg(self, message):
""" """
Обрабатывает информацию в пересланном сообщении (от кого оно и/или из какого канала). Требует дополнительно Обрабатывает информацию в пересланном сообщении (от кого оно и/или из какого канала). Требует дополнительно
@@ -267,16 +283,11 @@ class TelegramGateClient(TelegramClient):
""" """
if message.fwd_from.from_id: # От пользователя if message.fwd_from.from_id: # От пользователя
usr = self.get_entity(message.fwd_from.from_id) if not message.fwd_from.from_id in self.xmpp_gate.tg_dialogs[self.jid]['users'] else self.xmpp_gate.tg_dialogs[self.jid]['users'][message.fwd_from.from_id] usr = self._get_user_information(message.fwd_from.from_id)
print(usr)
if usr.access_hash:
self.xmpp_gate.tg_dialogs[self.jid]['users'][message.fwd_from.from_id] = usr
fwd_from = display_tg_name(usr.first_name, usr.last_name) fwd_from = display_tg_name(usr.first_name, usr.last_name)
else:
fwd_from = '<Unknown User>'
if message.fwd_from.channel_id: # От канала if message.fwd_from.channel_id: # От канала
fwd_from = '<Channel {}>'.format(message.fwd_from.channel_id) fwd_from = 'Channel {}'.format(message.fwd_from.channel_id)
# let's construct # let's construct
fwd_reply = '|Forwarded from [{}]|'.format(fwd_from) fwd_reply = '|Forwarded from [{}]|'.format(fwd_from)
@@ -362,120 +373,65 @@ class TelegramGateClient(TelegramClient):
return msg return msg
@staticmethod def _process_info_msg(self, message, peer):
def _process_info_msg(message, users):
""" """
Обрабатывает информационные сообщения в групповых чатах. Возвращает готовое для вывода сообщение. Обрабатывает информационные сообщения в групповых чатах. Возвращает готовое для вывода сообщение.
:param message: :param message:
:param users: :param users:
:return: :return:
""" """
alt_msg = None
nickname = display_tg_name(users[0].first_name, users[0].last_name)
uid = users[0].id
# MessageActionChatEditPhoto msg = ''
usr = self._get_user_information(message.from_id)
nickname = display_tg_name(usr.first_name, usr.last_name)
# Создана супергруппа # supergroup created #
if type(message.action) is MessageActionChannelCreate: if type(message.action) is MessageActionChannelCreate:
# Пока нет смысла - поддержка каналов не реализована
pass pass
# Создана группа
# group created #
elif type(message.action) is MessageActionChatCreate: elif type(message.action) is MessageActionChatCreate:
pass pass
# Добавлен пользователь в чат
# user added #
elif type(message.action) is MessageActionChatAddUser: elif type(message.action) is MessageActionChatAddUser:
if len(users) == 2: # Кто-то добавил другого пользователя added_users = []
j_name = display_tg_name(users[1].first_name, users[1].last_name) for user_id in message.action.users:
j_uid = users[1].id usr = self._get_user_information(user_id)
alt_msg = 'User [{}] (UID:{}) added [{}] (UID:{})'.format(nickname, uid, added_users.append(display_tg_name(usr.first_name, usr.last_name))
j_name, j_uid)
else: # Пользователь вошел сам msg = 'User [{}] has just invited [{}]'.format(nickname, ','.join(added_users))
alt_msg = 'User [{}] (UID:{}) joined'.format(nickname, uid)
# Пользователь удален/вышел/забанен # user exit #
elif type(message.action) is MessageActionChatDeleteUser: elif type(message.action) is MessageActionChatDeleteUser:
pass usr = self._get_user_information(message.action.user_id)
# Пользователь вошел по инвайт ссылке msg = 'User [{}] has just left the room'.format(display_tg_name(usr.first_name, usr.last_name))
# user joined #
elif type(message.action) is MessageActionChatJoinedByLink: elif type(message.action) is MessageActionChatJoinedByLink:
alt_msg = 'User [{}] (UID:{}) joined via invite link'.format(nickname, uid) usr = self._get_user_information(message.action.user_id)
# Изменено название чата msg = 'User [{}] joined the room'.format(display_tg_name(usr.first_name, usr.last_name))
# chat name modified #
elif type(message.action) is MessageActionChatEditTitle: elif type(message.action) is MessageActionChatEditTitle:
g_title = message.action.title msg = 'User [{}] changed title to [{}]'.format(nickname, message.action.title)
alt_msg = 'User [{}] (UID:{}) changed title to [{}]'.format(nickname, uid, g_title)
# Прикреплено сообщение в чате # pinned message
elif type(message.action) is MessageActionPinMessage: elif type(message.action) is MessageActionPinMessage:
# Notify all members реализовано путем указания, что пользователя упомянули, pinned_mid = message.reply_to_msg_id # target message
# то есть флаг mentioned=True. Но для транспорта он не имеет смысла. message_req = self.invoke(GetMessagesRequest(peer, [pinned_mid]))
p_mid = message.reply_to_msg_id # Наркоманы if len(message_req.messages) > 0:
alt_msg = 'User [{}] (UID:{}) pinned message with MID:{}'.format(nickname, uid, p_mid) pinned_message = message_req.messages[0].message
# Группа была преобразована в супергруппу pinned_from = self._get_user_information(message_req.messages[0].from_id)
elif type(message.action) is MessageActionChatMigrateTo: msg = 'User [{}] pinned message: [{}]: {}'.format(nickname, display_tg_name(pinned_from.first_name, pinned_from.last_name), pinned_message)
# Это сложный ивент, который ломает текущую реализацию хендлинга
# (ибо в доках, которых нет, не сказано, что так можно было)
# Пусть полежит до рефакторинга
pass
# Супергруппа была технически создана из группы
elif type(message.action) is MessageActionChannelMigrateFrom:
# ---...---...---
# ---...---...---
# ---...---...---
pass
return alt_msg # group converted to supergroup
elif type(message.action) in [MessageActionChatMigrateTo, MessageActionChannelMigrateFrom]:
print('--ACHTUNG!--')
print(message)
#del(self.xmpp_gate.tg_dialogs[self.jid]['groups'][message.chat_id])
def get_cached_message(self, dlg_id, msg_id, user=False, group=False, supergroup=False): return msg
"""
Получает из кэша сообщение диалога указанной группы (для работы цитат в последних сообщениях)
:param dlg_id:
:param msg_id:
:param user:
:param group:
:param supergroup:
:return:
"""
if user:
obj = self._message_cache_users
elif group:
obj = self._message_cache_groups
elif supergroup:
obj = self._message_cache_supergroups
else:
return None
if dlg_id in obj:
if msg_id in obj[dlg_id]:
return obj[dlg_id][msg_id]
return None
def set_cached_message(self, dlg_id, msg_id, msg, user=False, group=False, supergroup=False):
"""
Кэширует сообщение из диалога указанной группы (для работы цитат в последних сообщениях)
:param dlg_id:
:param msg_id:
:param msg:
:param user:
:param group:
:param supergroup:
:return:
"""
if user:
obj = self._message_cache_users
elif group:
obj = self._message_cache_groups
elif supergroup:
obj = self._message_cache_supergroups
else:
return
if dlg_id not in obj:
obj[dlg_id] = dict()
obj[dlg_id][msg_id] = msg
# Удаляем старые сообщения из кэша
if len(obj[dlg_id]) > self.xmpp_gate.config['messages_max_max_cache_size']:
del obj[dlg_id][sorted(obj[dlg_id].keys())[0]]
def media_thread_downloader(self): def media_thread_downloader(self):
""" """