[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
from telethon.tl.types import DocumentAttributeAnimated, DocumentAttributeAudio, DocumentAttributeFilename,\
DocumentAttributeSticker, DocumentAttributeVideo, DocumentAttributeHasStickers
from telethon.tl.types import MessageService, MessageActionChannelCreate, MessageActionChannelMigrateFrom,\
from telethon.tl.types import Message, MessageService, MessageActionChannelCreate, MessageActionChannelMigrateFrom,\
MessageActionChatCreate, MessageActionChatAddUser, MessageActionChatDeleteUser,\
MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo,\
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 PeerUser, PeerChat, PeerChannel
from telethon.tl.functions.users import GetFullUserRequest
from telethon.tl.functions.messages import ReadHistoryRequest, GetFullChatRequest
from telethon.tl.functions.channels import ReadHistoryRequest as ReadHistoryChannel, GetParticipantRequest
from telethon.tl.functions.messages import ReadHistoryRequest, GetFullChatRequest, GetMessagesRequest
from telethon.tl.functions.channels import ReadHistoryRequest as ReadHistoryChannel, GetParticipantRequest, GetMessagesRequest
from telethon.tl.functions.updates import GetDifferenceRequest
from telethon.tl.functions.contacts import ResolveUsernameRequest
@@ -62,8 +62,10 @@ class TelegramGateClient(TelegramClient):
print('new update for ' + self.jid)
print(type(obj), obj.__dict__)
# link to self-user #
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
self.gate_send_message( mfrom='u' + str(obj.user_id), mbody = '[MSG {}] {}{}'.format(obj.id, fwd_from, obj.message) )
if obj.user_id in self.xmpp_gate.tg_dialogs[self.jid]['users']: # make as read
usr = self.xmpp_gate.tg_dialogs[self.jid]['users'][obj.user_id]
self.invoke(ReadHistoryRequest( InputPeerUser(usr.id, usr.access_hash), obj.id ))
usr = self._get_user_information(obj.user_id) # get peer information
self.invoke(ReadHistoryRequest( InputPeerUser(usr.id, usr.access_hash), obj.id )) # delivery report
# message from normal group #
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:
cid = None
mid = obj.message.id
msg = obj.message.message
msg = ''
fwd_from = ''
mid = obj.message.id
# detect message type
is_user = type(obj.message.to_id) is PeerUser
is_group = type(obj.message.to_id) is PeerChat
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
if is_user:
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'
elif is_group:
cid = obj.message.to_id.chat_id
peer = InputPeerChat(cid)
prefix = 'g'
elif is_supergroup:
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'
# 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? #
if obj.message.post:
prefix = 'c'
# maybe its forwarded? #
# get sender information from chat info #
if not is_user and not obj.message.post:
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)))
for usr in chat_info.users:
chat = self.invoke(GetFullChatRequest(cid)) if is_group else self.invoke(GetParticipantRequest(peer, self.me))
for usr in chat.users:
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)
@@ -171,20 +180,14 @@ class TelegramGateClient(TelegramClient):
self.gate_send_message(prefix + str(cid), mbody = '[MSG {}] {}{}'.format(mid, fwd_from, msg) )
# delivery report
if is_user and usr.access_hash: # make as read
self.invoke(ReadHistoryRequest( InputPeerUser(usr.id, usr.access_hash), mid ))
if is_group:
self.invoke(ReadHistoryRequest(InputPeerChat(cid), mid))
if is_supergroup and access_hash:
self.invoke(ReadHistoryChannel(InputPeerChannel(cid, access_hash), mid))
if is_supergroup:
self.invoke(ReadHistoryChannel(peer, mid))
else:
self.invoke(ReadHistoryRequest(peer, mid))
# Status Updates #
if type(obj) is UpdateUserStatus:
print(self._status_last)
print(time.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 ):
return
@@ -256,6 +259,19 @@ class TelegramGateClient(TelegramClient):
return attrib
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):
"""
Обрабатывает информацию в пересланном сообщении (от кого оно и/или из какого канала). Требует дополнительно
@@ -267,16 +283,11 @@ class TelegramGateClient(TelegramClient):
"""
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]
print(usr)
if usr.access_hash:
self.xmpp_gate.tg_dialogs[self.jid]['users'][message.fwd_from.from_id] = usr
usr = self._get_user_information(message.fwd_from.from_id)
fwd_from = display_tg_name(usr.first_name, usr.last_name)
else:
fwd_from = '<Unknown User>'
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
fwd_reply = '|Forwarded from [{}]|'.format(fwd_from)
@@ -362,120 +373,65 @@ class TelegramGateClient(TelegramClient):
return msg
@staticmethod
def _process_info_msg(message, users):
def _process_info_msg(self, message, peer):
"""
Обрабатывает информационные сообщения в групповых чатах. Возвращает готовое для вывода сообщение.
:param message:
:param users:
: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:
# Пока нет смысла - поддержка каналов не реализована
pass
# Создана группа
# group created #
elif type(message.action) is MessageActionChatCreate:
pass
# Добавлен пользователь в чат
# user added #
elif type(message.action) is MessageActionChatAddUser:
if len(users) == 2: # Кто-то добавил другого пользователя
j_name = display_tg_name(users[1].first_name, users[1].last_name)
j_uid = users[1].id
alt_msg = 'User [{}] (UID:{}) added [{}] (UID:{})'.format(nickname, uid,
j_name, j_uid)
else: # Пользователь вошел сам
alt_msg = 'User [{}] (UID:{}) joined'.format(nickname, uid)
# Пользователь удален/вышел/забанен
added_users = []
for user_id in message.action.users:
usr = self._get_user_information(user_id)
added_users.append(display_tg_name(usr.first_name, usr.last_name))
msg = 'User [{}] has just invited [{}]'.format(nickname, ','.join(added_users))
# user exit #
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:
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:
g_title = message.action.title
alt_msg = 'User [{}] (UID:{}) changed title to [{}]'.format(nickname, uid, g_title)
# Прикреплено сообщение в чате
msg = 'User [{}] changed title to [{}]'.format(nickname, message.action.title)
# pinned message
elif type(message.action) is MessageActionPinMessage:
# Notify all members реализовано путем указания, что пользователя упомянули,
# то есть флаг mentioned=True. Но для транспорта он не имеет смысла.
p_mid = message.reply_to_msg_id # Наркоманы
alt_msg = 'User [{}] (UID:{}) pinned message with MID:{}'.format(nickname, uid, p_mid)
# Группа была преобразована в супергруппу
elif type(message.action) is MessageActionChatMigrateTo:
# Это сложный ивент, который ломает текущую реализацию хендлинга
# (ибо в доках, которых нет, не сказано, что так можно было)
# Пусть полежит до рефакторинга
pass
# Супергруппа была технически создана из группы
elif type(message.action) is MessageActionChannelMigrateFrom:
# ---...---...---
# ---...---...---
# ---...---...---
pass
pinned_mid = message.reply_to_msg_id # target message
message_req = self.invoke(GetMessagesRequest(peer, [pinned_mid]))
if len(message_req.messages) > 0:
pinned_message = message_req.messages[0].message
pinned_from = self._get_user_information(message_req.messages[0].from_id)
msg = 'User [{}] pinned message: [{}]: {}'.format(nickname, display_tg_name(pinned_from.first_name, pinned_from.last_name), pinned_message)
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):
"""
Получает из кэша сообщение диалога указанной группы (для работы цитат в последних сообщениях)
: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]]
return msg
def media_thread_downloader(self):
"""