diff --git a/xmpp_tg/mtproto.py b/xmpp_tg/mtproto.py index 42a17d5..52cdc57 100644 --- a/xmpp_tg/mtproto.py +++ b/xmpp_tg/mtproto.py @@ -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 + msg = '' + fwd_from = '' mid = obj.message.id - msg = obj.message.message - fwd_from = '' + # 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 @@ -255,6 +258,19 @@ class TelegramGateClient(TelegramClient): if type(attrib) == match: 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 - fwd_from = display_tg_name(usr.first_name, usr.last_name) - else: - fwd_from = '' + usr = self._get_user_information(message.fwd_from.from_id) + fwd_from = display_tg_name(usr.first_name, usr.last_name) if message.fwd_from.channel_id: # От канала - fwd_from = ''.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 + + msg = '' + usr = self._get_user_information(message.from_id) + nickname = display_tg_name(usr.first_name, usr.last_name) - # MessageActionChatEditPhoto - - # Создана супергруппа + # 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) - # Пользователь удален/вышел/забанен - elif type(message.action) is MessageActionChatDeleteUser: - pass - # Пользователь вошел по инвайт ссылке + 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: + 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): """