def _process_forward_msg(message, users, channels): """ Обрабатывает информацию в пересланном сообщении (от кого оно и/или из какого канала). Требует дополнительно предоставление информации об пользователях/каналах. :param message: :param users: :param channels: :return: """ if message.fwd_from.from_id: # От пользователя fwd_user_id = message.fwd_from.from_id fwd_user = users[fwd_user_id] fwd_user_nickname = display_tg_name(fwd_user.first_name, fwd_user.last_name) if message.fwd_from.channel_id: # От канала fwd_channel_id = message.fwd_from.channel_id fwd_channel_title = channels[fwd_channel_id].title # Теперь строим if message.fwd_from.from_id and message.fwd_from.channel_id: # Неанонимное сообщение в канале fwd_reply = '|Forwarded from [{}] (UID:{}) at [{}] (CID:{})|Views: {}'\ .format(fwd_user_nickname, fwd_user_id, fwd_channel_title, fwd_channel_id, message.views) elif message.fwd_from.from_id: # Сообщение пользователя fwd_reply = '|Forwarded from [{}] (UID:{})'.format( fwd_user_nickname, fwd_user_id) elif message.fwd_from.channel_id: # Анонимное сообщение в канале fwd_reply = '|Forwarded from [{}] (CID:{})|Views: {}' \ .format(fwd_channel_title, fwd_channel_id, message.views) return fwd_reply
def _process_forward_msg(self, message): """ Process forward message to find out from what user message is forwarded. :param message: :param users: :param channels: :return: """ if message.fwd_from.from_id: # from user usr = self._get_user_information(message.fwd_from.from_id) fwd_from = display_tg_name(usr) if message.fwd_from.channel_id: # from channel fwd_from = 'Channel {}'.format(message.fwd_from.channel_id) # let's construct fwd_reply = '|Forwarded from [{}]|'.format(fwd_from) return fwd_reply
def xmpp_update_handler(self, obj): """ Main function: Telegram update handler. :param media: :return: """ # print("We have received update for <%s>" % self.jid) # print(obj) # we have received some updates, so we're logined and can get <me> object and start mtd / upd threads # if not self.me: me = self.get_me() self.me = InputPeerUser(me.id, me.access_hash) self._media_thread.start() self._status_update_thread.start() nl = '\n' try: # message from normal chat # if type(obj) in [UpdateShortMessage] and not obj.out: 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)) 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: fwd_from = self._process_forward_msg( obj) if obj.fwd_from else '' # process forward messages usr = self._get_user_information(obj.from_id) nickname = display_tg_name(usr) # send message self.gate_send_message(mfrom='g' + str(obj.chat_id), mbody='[MSG {}] [User: {}] {}{}'.format( obj.id, nickname, fwd_from, obj.message)) self.invoke( ReadHistoryRequest(InputPeerChat(obj.chat_id), obj.id)) # message from supergroup or media message # if type(obj) in [ UpdateNewMessage, UpdateNewChannelMessage, UpdateEditMessage, UpdateEditChannelMessage ] and not obj.message.out: cid = None 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 # detect from id if is_user: cid = obj.message.from_id user = self._get_user_information(cid) peer = InputPeerUser(user.id, user.access_hash) prefix = 'u' prefix = 'b' if user.bot else prefix 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 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' # get sender information from chat info # if not is_user and not obj.message.post: usr = self._get_user_information(obj.message.from_id) nickname = display_tg_name(usr) msg = '[User: {}] {}'.format(nickname, msg) # message media # if obj.message.media: msg = '{} {}'.format( msg, self._process_media_msg(obj.message.media)) # edited # if obj.message.edit_date: msg = '[Edited] {}'.format(msg) # send message # self.gate_send_message(prefix + str(cid), mbody='[MSG {}] {}{}'.format( mid, fwd_from, msg)) # delivery report if is_supergroup: self.invoke(ReadHistoryChannel(peer, mid)) else: self.invoke(ReadHistoryRequest(peer, mid)) # Status Updates # if type(obj) is UpdateUserStatus: # process status update # if type(obj.status) is UserStatusOnline: self._status_updates[str(obj.user_id)] = { 'status': None, 'message': 'Online' } elif type(obj.status) is UserStatusOffline: status = 'away' if datetime.datetime.utcnow( ) - obj.status.was_online < datetime.timedelta( hours=self.xmpp_gate.accounts[ self.jid]['status_xa_interval']) else 'xa' self._status_updates[str(obj.user_id)] = { 'status': status, 'message': localtime(obj.status.was_online).strftime( 'Last seen at %H:%M %d/%m/%Y') } elif type(obj.status) is UserStatusRecently: self._status_updates[str(obj.user_id)] = { 'status': 'dnd', 'message': 'Last seen recently' } else: pass except Exception: print('Exception occurs!') print(traceback.format_exc())
def _process_info_msg(self, message, peer): """ Information messages. :param message: :param users: :return: """ msg = '' usr = self._get_user_information(message.from_id) nickname = display_tg_name(usr) # 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: added_users = [] for user_id in message.action.users: usr = self._get_user_information(user_id) added_users.append(display_tg_name(usr)) 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)) # user joined # elif type(message.action) is MessageActionChatJoinedByLink: usr = self._get_user_information(message.action.user_id) msg = 'User [{}] joined the room'.format(display_tg_name(usr)) # chat name modified # elif type(message.action) is MessageActionChatEditTitle: msg = 'User [{}] changed title to [{}]'.format( nickname, message.action.title) # pinned message elif type(message.action) is MessageActionPinMessage: 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), pinned_message) # group converted to supergroup elif type(message.action) in [ MessageActionChatMigrateTo, MessageActionChannelMigrateFrom ]: pass return msg
def xmpp_update_handler(self, obj): print('new update for ' + self.jid) print(type(obj), obj.__dict__) ''' Боты Сделать запоминание ростера в бд Сделать лучше хендлинг ошибок Доделать все типы информационных сообщений Сделать джойны по линкам в чаты/каналы Сделать поиск и добавление пользователей Сделать листание истории Сделать отправку всех непрочтенных сообщений при входе ''' # Здесь будет очень длинный пиздец ^__^ nl = '\n' if self.user_options['nl_after_info'] else '' try: if type( obj ) is Updates: # Какой-то общий тип обновления (всегда большое со списками) print('Updates') # Делаем разбор пользователей/чатов, которые учавствуют в апдейте updates_users = {usr.id: usr for usr in obj.users} updates_groups = {} updates_channels = {} updates_supergroups = {} updates_type_channels = {} # Супегруппы и каналы вместе for chat in obj.chats: if type(chat) is Chat: # Обычная группа updates_groups[chat.id] = chat elif type(chat) is Channel: if chat.broadcast: # Канал updates_channels[chat.id] = chat else: # Супегруппа updates_supergroups[chat.id] = chat updates_type_channels[chat.id] = chat # ------------------------------------------------------------------------------------------- for update in obj.updates: # Апдейт содержит список с апдейтами # Новое сообщение или отредактированное в супегруппе или канале # А так же отредактированные и новые сообщения с медиа в ЛС и группах if type(update) in [ UpdateNewChannelMessage, UpdateEditChannelMessage, UpdateNewMessage, UpdateEditMessage ]: if update.message.out: # Игнорируем исходящее сообщение return uid = update.message.from_id # Будет None, если канал, а так же post=True mid = update.message.id cid = None is_post = update.message.post usr = updates_users[uid] if uid else None nickname = display_tg_name( usr.first_name, usr.last_name) if usr else None from_type = 'c' if is_post else 's' msg = '' alt_msg = None edited = '' fwd_reply = '' orig_msg = None is_user = type(update.message.to_id) is PeerUser is_group = type(update.message.to_id) is PeerChat is_supergroup = type( update.message.to_id) is PeerChannel if is_user: cid = update.message.from_id elif is_group: cid = update.message.to_id.chat_id elif is_supergroup: cid = update.message.to_id.channel_id if type( update.message ) is MessageService: # Сервисные уведомления в чате print('messageService detected') with open( '/home/sofia/tgdebug/' + str(mid) + '.txt', 'w') as f: f.write(var_dump(obj)) ################################################################## alt_msg = self._process_info_msg( update.message, obj.users) else: # Обычное сообщение в чате msg = update.message.message if update.message.media: print('media detected') ####################################################### msg = '[{}] {}'.format( update.message.media.__class__.__name__, '{} {}'.format( self._process_media_msg( update.message.media), msg)) if update.message.reply_to_msg_id: fwd_reply = '|Reply to MID: {}'.format( update.message.reply_to_msg_id) reply_mid = update.message.reply_to_msg_id orig_msg = self.get_cached_message( cid, reply_mid, is_user, is_group, is_supergroup) if update.message.fwd_from: # Пересланное сообщение fwd_reply = self._process_forward_msg( update.message, updates_users, updates_channels) if update.message.edit_date: # Если новое - отмечаем прочитанным edited = '|Edited' if alt_msg is None: if is_post or type( update.message.to_id) is PeerUser: header = '[MID:{}{}{}] '.format( mid, fwd_reply, edited) else: header = '[User: {}|UID:{}|MID:{}{}{}] {}'\ .format(nickname, uid, mid, fwd_reply, edited, nl) alt_msg = '{}{}'.format(header, msg) self.set_cached_message( # Кэшируем без цитаты cid, mid, alt_msg, user=is_user, group=is_group, supergroup=is_supergroup) if orig_msg is not None: # Перестраиваем сообщение уже с цитатой alt_msg = '{}> {}\n{}'.format( header, orig_msg.replace('\n', '\n> '), msg) if is_user: self.gate_send_message(mfrom='u' + str(cid), mbody=alt_msg) if False: # Зарезервируем рекурсивные цитаты под опцию self.set_cached_message(cid, mid, alt_msg, user=True) if not update.message.edit_date: self.invoke( ReadHistoryRequest( # Отмечаем прочитанным InputPeerUser(usr.id, usr.access_hash), mid)) elif is_group: self.gate_send_message( mfrom='g' + str(update.message.to_id.chat_id), mbody=alt_msg) if False: # ... self.set_cached_message(cid, mid, alt_msg, group=True) if not update.message.edit_date: self.invoke( ReadHistoryRequest(InputPeerChat(cid), mid)) elif is_supergroup: self.gate_send_message(mfrom=from_type + str(cid), mbody=alt_msg) if False: # ... self.set_cached_message(cid, mid, alt_msg, supergroup=True) if not update.message.edit_date: access_hash = updates_type_channels[ cid].access_hash self.invoke( ReadHistoryChannel( InputPeerChannel(cid, access_hash), mid)) elif type( update ) is UpdateDeleteChannelMessages: # Удаленные сообщения в супергруппе/канале channel_id = update.channel_id channel_type = 's' if self.xmpp_gate.tg_dialogs[self.jid]['supergroups'][ channel_id].broadcast: # А может канал? channel_type = 'c' self.gate_send_message( mfrom=channel_type + str(channel_id), mbody='[Deleted messages IDs: {}]'.format( ', '.join( [str(mid) for mid in update.messages]))) elif type(update) is UpdateDeleteMessages: # Этот ивент обновления присылается при удалении сообщения в личном сообщении или группе. # Только id сообщения. Нет информации об диалоге/пользователе/группе. pass if type(obj.updates) is list and type( obj.updates[0]) is UpdateChannelTooLong: print('too long') # ***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^*** if type( obj ) is UpdateShort: # Тоже какой-то общий тип обновления (всегда маленькое) if type( obj.update ) is UpdateUserStatus: # Обновление статуса пользователя в сети Tg print('UpdateUserStatus') if type(obj.update.status) is UserStatusOnline: self.xmpp_gate.send_presence( pto=self.jid, pfrom='u' + str(obj.update.user_id) + '@' + self.xmpp_gate.config['jid']) elif type(obj.update.status) is UserStatusOffline: self.xmpp_gate.send_presence( pto=self.jid, pfrom='u' + str(obj.update.user_id) + '@' + self.xmpp_gate.config['jid'], ptype='xa', pstatus=obj.update.status.was_online.strftime( 'Last seen at %H:%M %d/%m/%Y')) elif type(obj.update.status) is UserStatusRecently: self.xmpp_gate.send_presence( pto=self.jid, pfrom='u' + str(obj.update.user_id) + '@' + self.xmpp_gate.config['jid'], pstatus='Last seen recently') else: print(type(obj.update.status)) print(obj.update.status.__dict__) elif type( obj.update ) is UpdateDeleteChannelMessages: # Удаленное сообщение в супергруппе if obj.update.pts > self._del_pts: # Фильтруем дубли обновлений channel_id = obj.update.channel_id channel_type = 's' if self.xmpp_gate.tg_dialogs[ self.jid]['supergroups'][channel_id].broadcast: channel_type = 'c' msg = '[Deleted messages IDs: {}]'.format(', '.join( [str(mid) for mid in obj.update.messages])) self.gate_send_message(mfrom=channel_type + str(channel_id), mbody=msg) self._del_pts = obj.update.pts # ***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^***^__^*** # Входящее сообщение в ЛС или группу (без медиа вложений) if type(obj) in [UpdateShortMessage, UpdateShortChatMessage ] and not obj.out: fwd_reply = '' nickname = '' if type(obj) is UpdateShortChatMessage: # Так как в апдейте есть только ID пользователя, то запрашиваем с сервера информацию о группе if obj.from_id not in self._groups_users: chat_info = self.invoke(GetFullChatRequest( obj.chat_id)) for usr in chat_info.users: self._groups_users[usr.id] = usr nickname = display_tg_name( self._groups_users[obj.from_id].first_name, self._groups_users[obj.from_id].last_name) if obj.reply_to_msg_id: fwd_reply = '|Reply to MID: {}'.format(obj.reply_to_msg_id) if obj.fwd_from: full_update = self.invoke( GetDifferenceRequest(obj.pts - 1, obj.date, -1, 1)) fwd_reply = self._process_forward_msg( full_update.new_messages[0], {usr.id: usr for usr in full_update.users}, {}) if type(obj) is UpdateShortMessage: self.gate_send_message(mfrom='u' + str(obj.user_id), mbody='[MID:{}{}] {}'.format( obj.id, fwd_reply, obj.message)) if obj.user_id in self.xmpp_gate.tg_dialogs[ self.jid]['users']: usr = self.xmpp_gate.tg_dialogs[self.jid]['users'][ obj.user_id] self.invoke( ReadHistoryRequest( # Отмечаем прочитанным InputPeerUser(usr.id, usr.access_hash), obj.id)) elif type(obj) is UpdateShortChatMessage: self.gate_send_message( mfrom='g' + str(obj.chat_id), mbody='[User: {}|UID:{}|MID:{}{}] {}'.format( nickname, obj.from_id, obj.id, fwd_reply, obj.message)) self.invoke( ReadHistoryRequest(InputPeerChat(obj.chat_id), obj.id)) except Exception: print('Exception occurs!') print(traceback.format_exc()) print(' ')
def _process_info_msg(message, users): """ Обрабатывает информационные сообщения в групповых чатах. Возвращает готовое для вывода сообщение. :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 # Создана супергруппа if type(message.action) is MessageActionChannelCreate: # Пока нет смысла - поддержка каналов не реализована pass # Создана группа elif type(message.action) is MessageActionChatCreate: pass # Добавлен пользователь в чат 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 # Пользователь вошел по инвайт ссылке elif type(message.action) is MessageActionChatJoinedByLink: alt_msg = 'User [{}] (UID:{}) joined via invite link'.format( nickname, uid) # Изменено название чата elif type(message.action) is MessageActionChatEditTitle: g_title = message.action.title alt_msg = 'User [{}] (UID:{}) changed title to [{}]'.format( nickname, uid, g_title) # Прикреплено сообщение в чате 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 return alt_msg
def tg_process_dialogs(self, jid): # Инициализируем словари для диалогов self.tg_dialogs[jid] = dict() self.tg_dialogs[jid]['raw'] = list() self.tg_dialogs[jid]['users'] = dict() self.tg_dialogs[jid]['groups'] = dict() self.tg_dialogs[jid]['supergroups'] = dict() # Оффсеты для получения диалогов last_peer = InputPeerEmpty() last_msg_id = 0 last_date = None while True: # В цикле по кускам получаем все диалоги dlgs = self.tg_connections[jid].invoke( GetDialogsRequest(offset_date=last_date, offset_id=last_msg_id, offset_peer=last_peer, limit=100)) self.tg_dialogs[jid]['raw'].append(dlgs) for usr in dlgs.users: self.tg_dialogs[jid]['users'][usr.id] = usr for cht in dlgs.chats: if type(cht) in [Chat, ChatForbidden]: # Старая группа self.tg_dialogs[jid]['groups'][cht.id] = cht elif type(cht) in [Channel, ChannelForbidden]: # Супергруппа self.tg_dialogs[jid]['supergroups'][cht.id] = cht for dlg in dlgs.dialogs: if type(dlg.peer) is PeerUser: usr = self.tg_dialogs[jid]['users'][dlg.peer.user_id] vcard = self.plugin['xep_0054'].make_vcard() u_jid = 'u' + str(usr.id) + '@' + self.boundjid.bare if usr.deleted: vcard['FN'] = 'Deleted account' vcard[ 'DESC'] = 'This user no longer exists in Telegram' else: vcard['FN'] = display_tg_name(usr.first_name, usr.last_name) if usr.first_name: vcard['N']['GIVEN'] = usr.first_name if usr.last_name: vcard['N']['FAMILY'] = usr.last_name if usr.username: vcard[ 'DESC'] = 'Telegram Username: @' + usr.username if usr.bot: vcard['DESC'] += ' [Bot]' vcard['NICKNAME'] = vcard['FN'] vcard['JABBERID'] = u_jid self.plugin['xep_0054'].publish_vcard(jid=u_jid, vcard=vcard) self.plugin['xep_0172'].publish_nick(nick=vcard['FN'], ifrom=u_jid) self.send_presence(pto=jid, pfrom=u_jid, ptype='subscribe') if usr.bot: self.send_presence(pto=jid, pfrom=u_jid, pstatus='Bot') else: if type(usr.status) is UserStatusOnline: self.send_presence(pto=jid, pfrom=u_jid) elif type(usr.status) is UserStatusRecently: self.send_presence(pto=jid, pfrom=u_jid, pshow='away', pstatus='Last seen recently') elif type(usr.status) is UserStatusOffline: self.send_presence( pto=jid, pfrom=u_jid, ptype='xa', pstatus=usr.status.was_online.strftime( 'Last seen at %H:%M %d/%m/%Y')) else: self.send_presence( pto=jid, pfrom=u_jid, ptype='unavailable', pstatus='Last seen a long time ago') if type(dlg.peer) in [PeerChat, PeerChannel]: g_type = '' cht = None if type(dlg.peer) is PeerChat: # Старая группа cht = self.tg_dialogs[jid]['groups'][dlg.peer.chat_id] c_jid = 'g' + str(cht.id) + '@' + self.boundjid.bare g_type = 'G' elif type(dlg.peer) is PeerChannel: # Супергруппа cht = self.tg_dialogs[jid]['supergroups'][ dlg.peer.channel_id] if cht.broadcast: g_type = 'C' c_jid = 'c' + str( cht.id) + '@' + self.boundjid.bare else: g_type = 'SG' c_jid = 's' + str( cht.id) + '@' + self.boundjid.bare vcard = self.plugin['xep_0054'].make_vcard() vcard['FN'] = '[{}] {}'.format(g_type, cht.title) vcard['NICKNAME'] = vcard['FN'] vcard['JABBERID'] = c_jid self.plugin['xep_0054'].publish_vcard(jid=c_jid, vcard=vcard) self.plugin['xep_0172'].publish_nick(nick=vcard['FN'], ifrom=c_jid) self.send_presence(pto=jid, pfrom=c_jid, ptype='subscribe') self.send_presence(pto=jid, pfrom=c_jid) if len(dlgs.dialogs ) == 0: # Если все диалоги получены - прерываем цикл break else: # Иначе строим оффсеты last_msg_id = dlgs.dialogs[ -1].top_message # Нужен последний id сообщения. Наркоманы. last_peer = dlgs.dialogs[-1].peer last_date = next( msg for msg in dlgs.messages # Ищем дату среди сообщений if type(msg.to_id) is type(last_peer) and msg.id == last_msg_id).date if type(last_peer) is PeerUser: # Пользователь access_hash = self.tg_dialogs[jid]['users'][ last_peer.user_id].access_hash last_peer = InputPeerUser(last_peer.user_id, access_hash) elif type(last_peer) in [Chat, ChatForbidden]: # Группа last_peer = InputPeerChat(last_peer.chat_id) elif type(last_peer) in [Channel, ChannelForbidden]: # Супергруппа access_hash = self.tg_dialogs[jid]['supergroups'][ last_peer.channel_id].access_hash last_peer = InputPeerChannel(last_peer.channel_id, access_hash)