686 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			686 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
 | |
| 
 | |
| import traceback
 | |
| 
 | |
| from common.tox_save import ToxSave
 | |
| from contacts.friend import Friend
 | |
| from contacts.group_chat import GroupChat
 | |
| from contacts.group_peer_contact import GroupPeerContact
 | |
| from groups.group_peer import GroupChatPeer
 | |
| from messenger.messages import *
 | |
| 
 | |
| # LOG=util.log
 | |
| global LOG
 | |
| import logging
 | |
| 
 | |
| LOG = logging.getLogger('app.'+__name__)
 | |
| 
 | |
| def LOG_ERROR(l): print('ERROR_: '+l)
 | |
| def LOG_WARN(l): print('WARN_: '+l)
 | |
| def LOG_INFO(l): print('INFO_: '+l)
 | |
| def LOG_DEBUG(l): print('DEBUG_: '+l)
 | |
| def LOG_TRACE(l): pass # print('TRACE+ '+l)
 | |
| 
 | |
| 
 | |
| UINT32_MAX = 2 ** 32 -1
 | |
| 
 | |
| def set_contact_kind(contact):
 | |
|     bInvite = len(contact.name) == TOX_PUBLIC_KEY_SIZE * 2 and \
 | |
|       contact.status_message == ''
 | |
|     bBot = not bInvite and contact.name.lower().endswith(' bot')
 | |
|     if type(contact) == Friend and bInvite:
 | |
|         contact._kind = 'invite'
 | |
|     elif type(contact) == Friend and bBot:
 | |
|         contact._kind = 'bot'
 | |
|     elif type(contact) == Friend:
 | |
|         contact._kind = 'friend'
 | |
|     elif type(contact) == GroupChat:
 | |
|         contact._kind = 'group'
 | |
|     elif type(contact) == GroupChatPeer:
 | |
|         contact._kind = 'grouppeer'
 | |
| 
 | |
| class ContactsManager(ToxSave):
 | |
|     """
 | |
|     Represents contacts list.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, tox, settings, screen, profile_manager, contact_provider, history, tox_dns,
 | |
|                  messages_items_factory):
 | |
|         super().__init__(tox)
 | |
|         self._settings = settings
 | |
|         self._screen = screen
 | |
|         self._ms = screen
 | |
|         self._profile_manager = profile_manager
 | |
|         self._contact_provider = contact_provider
 | |
|         self._tox_dns = tox_dns
 | |
|         self._messages_items_factory = messages_items_factory
 | |
|         self._messages = screen.messages
 | |
|         self._contacts = []
 | |
|         self._active_contact = -1
 | |
|         self._active_contact_changed = Event()
 | |
|         self._sorting = settings['sorting']
 | |
|         self._filter_string = ''
 | |
|         screen.contacts_filter.setCurrentIndex(int(self._sorting))
 | |
|         self._history = history
 | |
|         self._load_contacts()
 | |
| 
 | |
|     def _log(self, s):
 | |
|         try:
 | |
|             self._ms._log(s)
 | |
|         except: pass
 | |
| 
 | |
|     def get_contact(self, num):
 | |
|         if num < 0 or num >= len(self._contacts):
 | |
|             return None
 | |
|         return self._contacts[num]
 | |
| 
 | |
|     def get_curr_contact(self):
 | |
|         return self._contacts[self._active_contact] if self._active_contact + 1 else None
 | |
| 
 | |
|     def save_profile(self):
 | |
|         data = self._tox.get_savedata()
 | |
|         self._profile_manager.save_profile(data)
 | |
| 
 | |
|     def is_friend_active(self, friend_number):
 | |
|         if not self.is_active_a_friend():
 | |
|             return False
 | |
| 
 | |
|         return self.get_curr_contact().number == friend_number
 | |
| 
 | |
|     def is_group_active(self, group_number):
 | |
|         if self.is_active_a_friend():
 | |
|             return False
 | |
| 
 | |
|         return self.get_curr_contact().number == group_number
 | |
| 
 | |
|     def is_contact_active(self, contact):
 | |
|         if self._active_contact == -1:
 | |
| #            LOG.debug("No self._active_contact")
 | |
|             return False
 | |
|         if self._active_contact >= len(self._contacts):
 | |
|             LOG.warn(f"ERROR _active_contact={self._active_contact} >= contacts len={len(self._contacts)}")
 | |
|             return False
 | |
|         if not self._contacts[self._active_contact]:
 | |
|             LOG.warn(f"ERROR NULL {self._contacts[self._active_contact]}  {contact.tox_id}")
 | |
|             return False
 | |
| 
 | |
|         return self._contacts[self._active_contact].tox_id == contact.tox_id
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Reconnection support
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def reset_contacts_statuses(self):
 | |
|         for contact in self._contacts:
 | |
|             contact.status = None
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Work with active friend
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def get_active(self):
 | |
|         return self._active_contact
 | |
| 
 | |
|     def set_active(self, value):
 | |
|         """
 | |
|         Change current active friend or update info
 | |
|         :param value: number of new active friend in friend's list
 | |
|         """
 | |
|         if value is None and self._active_contact == -1:  # nothing to update
 | |
|             return
 | |
|         if value == -1:  # all friends were deleted
 | |
|             self._screen.account_name.setText('')
 | |
|             self._screen.account_status.setText('')
 | |
|             self._screen.account_status.setToolTip('')
 | |
|             self._active_contact = -1
 | |
|             self._screen.account_avatar.setHidden(True)
 | |
|             self._messages.clear()
 | |
|             self._screen.messageEdit.clear()
 | |
|             return
 | |
|         try:
 | |
|             self._screen.typing.setVisible(False)
 | |
|             current_contact = self.get_curr_contact()
 | |
|             if current_contact is not None:
 | |
|                 # TODO: send when needed
 | |
|                 current_contact.typing_notification_handler.send(self._tox, False)
 | |
|                 current_contact.remove_messages_widgets()  # TODO: if required
 | |
|                 self._unsubscribe_from_events(current_contact)
 | |
| 
 | |
|             if self._active_contact >= 0 and self._active_contact != value:
 | |
|                 try:
 | |
|                     current_contact.curr_text = self._screen.messageEdit.toPlainText()
 | |
|                 except:
 | |
|                     pass
 | |
|             # IndexError: list index out of range
 | |
|             contact = self._contacts[value]
 | |
|             self._subscribe_to_events(contact)
 | |
|             contact.remove_invalid_unsent_files()
 | |
|             if self._active_contact != value:
 | |
|                 self._screen.messageEdit.setPlainText(contact.curr_text)
 | |
|             self._active_contact = value
 | |
|             contact.reset_messages()
 | |
|             if not self._settings['save_history']:
 | |
|                 contact.delete_old_messages()
 | |
|             self._messages.clear()
 | |
|             contact.load_corr()
 | |
|             corr = contact.get_corr()[-PAGE_SIZE:]
 | |
|             for message in corr:
 | |
|                 if message.type == MESSAGE_TYPE['FILE_TRANSFER']:
 | |
|                     self._messages_items_factory.create_file_transfer_item(message)
 | |
|                 elif message.type == MESSAGE_TYPE['INLINE']:
 | |
|                     self._messages_items_factory.create_inline_item(message)
 | |
|                 else:
 | |
|                     self._messages_items_factory.create_message_item(message)
 | |
|             self._messages.scrollToBottom()
 | |
|             # if value in self._call:
 | |
|             #     self._screen.active_call()
 | |
|             # elif value in self._incoming_calls:
 | |
|             #     self._screen.incoming_call()
 | |
|             # else:
 | |
|             #     self._screen.call_finished()
 | |
|             self._set_current_contact_data(contact)
 | |
|             self._active_contact_changed(contact)
 | |
|         except Exception as ex:  # no friend found. ignore
 | |
|             LOG.warn(f"no friend found. Friend value: {value!s}")
 | |
|             LOG.error('in set active: ' + str(ex))
 | |
|             # gulp raise
 | |
| 
 | |
|     active_contact = property(get_active, set_active)
 | |
| 
 | |
|     def get_active_contact_changed(self):
 | |
|         return self._active_contact_changed
 | |
| 
 | |
|     active_contact_changed = property(get_active_contact_changed)
 | |
| 
 | |
|     def update(self):
 | |
|         if self._active_contact + 1:
 | |
|             self.set_active(self._active_contact)
 | |
| 
 | |
|     def is_active_a_friend(self):
 | |
|         return type(self.get_curr_contact()) is Friend
 | |
| 
 | |
|     def is_active_a_group(self):
 | |
|         return type(self.get_curr_contact()) is GroupChat
 | |
| 
 | |
|     def is_active_a_group_chat_peer(self):
 | |
|         return type(self.get_curr_contact()) is GroupPeerContact
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Filtration
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def filtration_and_sorting(self, sorting=0, filter_str=''):
 | |
|         """
 | |
|         Filtration of friends list
 | |
|         :param sorting: 0 - no sorting, 1 - online only, 2 - online first, 3 - by name,
 | |
|         4 - online and by name, 5 - online first and by name, 6 kind
 | |
|         :param filter_str: show contacts which name contains this substring
 | |
|         """
 | |
|         filter_str = filter_str.lower()
 | |
|         current_contact = self.get_curr_contact()
 | |
| 
 | |
|         for index, contact in enumerate(self._contacts):
 | |
|             if not contact._kind:
 | |
|                 set_contact_kind(contact)
 | |
| 
 | |
|         if sorting > 6 or sorting < 0:
 | |
|             sorting = 0
 | |
| 
 | |
|         if sorting in (1, 2, 4, 5):  # online first
 | |
|             self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True)
 | |
|             sort_by_name = sorting in (4, 5)
 | |
|             # save results of previous sorting
 | |
|             online_friends = filter(lambda x: x.status is not None, self._contacts)
 | |
|             online_friends_count = len(list(online_friends))
 | |
|             part1 = self._contacts[:online_friends_count]
 | |
|             part2 = self._contacts[online_friends_count:]
 | |
|             key_lambda = lambda x: x.name.lower() if sort_by_name else x.number
 | |
|             part1 = sorted(part1, key=key_lambda)
 | |
|             part2 = sorted(part2, key=key_lambda)
 | |
|             self._contacts = part1 + part2
 | |
|         elif sorting == 0:
 | |
|             # AttributeError: 'NoneType' object has no attribute 'number'
 | |
|             for (i, contact) in enumerate(self._contacts):
 | |
|                 if contact is None or not hasattr(contact, 'number'):
 | |
|                     LOG.error(f"Contact {i} is None or not hasattr 'number'")
 | |
|                     del self._contacts[i]
 | |
|                     continue
 | |
|             contacts = sorted(self._contacts, key=lambda c: c.number)
 | |
|             friends = filter(lambda c: type(c) is Friend, contacts)
 | |
|             groups = filter(lambda c: type(c) is GroupChat, contacts)
 | |
|             group_peers = filter(lambda c: type(c) is GroupPeerContact, contacts)
 | |
|             self._contacts = list(friends) + list(groups) + list(group_peers)
 | |
|         elif sorting == 6:
 | |
|             self._contacts = sorted(self._contacts, key=lambda x: x._kind)
 | |
|         else:
 | |
|             self._contacts = sorted(self._contacts, key=lambda x: x.name.lower())
 | |
| 
 | |
| 
 | |
|         # change item widgets
 | |
|         for index, contact in enumerate(self._contacts):
 | |
|             list_item = self._screen.friends_list.item(index)
 | |
|             item_widget = self._screen.friends_list.itemWidget(list_item)
 | |
|             contact.set_widget(item_widget)
 | |
| 
 | |
|         for index, friend in enumerate(self._contacts):
 | |
|             filtered_by_name = filter_str in friend.name.lower()
 | |
|             friend.visibility = (friend.status is not None or sorting not in (1, 4)) and filtered_by_name
 | |
|             # show friend even if it's hidden when there any unread messages/actions
 | |
|             friend.visibility = friend.visibility or friend.messages or friend.actions
 | |
|             item = self._screen.friends_list.item(index)
 | |
|             item_widget = self._screen.friends_list.itemWidget(item)
 | |
|             item.setSizeHint(QtCore.QSize(250, item_widget.height() if friend.visibility else 0))
 | |
| 
 | |
|         # save soring results
 | |
|         self._sorting, self._filter_string = sorting, filter_str
 | |
|         self._settings['sorting'] = self._sorting
 | |
|         self._settings.save()
 | |
| 
 | |
|         # update active contact
 | |
|         if current_contact is not None:
 | |
|             index = self._contacts.index(current_contact)
 | |
|             self.set_active(index)
 | |
| 
 | |
|     def update_filtration(self):
 | |
|         """
 | |
|         Update list of contacts when 1 of friends change connection status
 | |
|         """
 | |
|         self.filtration_and_sorting(self._sorting, self._filter_string)
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Contact getters
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def get_friend_by_number(self, number):
 | |
|         return list(filter(lambda c: c.number == number and type(c) is Friend, self._contacts))[0]
 | |
| 
 | |
|     def get_group_by_number(self, number):
 | |
|         return list(filter(lambda c: c.number == number and type(c) is GroupChat, self._contacts))[0]
 | |
| 
 | |
|     def get_or_create_group_peer_contact(self, group_number, peer_id):
 | |
|         group = self.get_group_by_number(group_number)
 | |
|         peer = group.get_peer_by_id(peer_id)
 | |
|         if peer: # broken?
 | |
|             if not hasattr(peer, 'public_key') or not peer.public_key:
 | |
|                 LOG.error(f'no peer public_key ' + repr(dir(peer)))
 | |
|             else:
 | |
|                 if not self.check_if_contact_exists(peer.public_key):
 | |
|                     self.add_group_peer(group, peer)
 | |
|                 return self.get_contact_by_tox_id(peer.public_key)
 | |
|         else:
 | |
|             LOG.warn(f'no peer group_number={group_number} peer_id={peer_id}')
 | |
| 
 | |
|     def check_if_contact_exists(self, tox_id):
 | |
|         return any(filter(lambda c: c.tox_id == tox_id, self._contacts))
 | |
| 
 | |
|     def get_contact_by_tox_id(self, tox_id):
 | |
|         return list(filter(lambda c: c.tox_id == tox_id, self._contacts))[0]
 | |
| 
 | |
|     def get_active_number(self):
 | |
|         return self.get_curr_contact().number if self._active_contact + 1 else -1
 | |
| 
 | |
|     def get_active_name(self):
 | |
|         return self.get_curr_contact().name if self._active_contact + 1 else ''
 | |
| 
 | |
|     def is_active_online(self):
 | |
|         return self._active_contact + 1 and self.get_curr_contact().status is not None
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Work with friends (remove, block, set alias, get public key)
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def set_alias(self, num):
 | |
|         """
 | |
|         Set new alias for friend
 | |
|         """
 | |
|         friend = self._contacts[num]
 | |
|         name = friend.name
 | |
|         text = util_ui.tr("Enter new alias for friend {} or leave empty to use friend's name:").format(name)
 | |
|         title = util_ui.tr('Set alias')
 | |
|         text, ok = util_ui.text_dialog(text, title, name)
 | |
|         if not ok:
 | |
|             return
 | |
|         aliases = self._settings['friends_aliases']
 | |
|         if text:
 | |
|             friend.name = text
 | |
|             try:
 | |
|                 index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
 | |
|                 aliases[index] = (friend.tox_id, text)
 | |
|             except:
 | |
|                 aliases.append((friend.tox_id, text))
 | |
|             friend.set_alias(text)
 | |
|         else:  # use default name
 | |
|             friend.name = self._tox.friend_get_name(friend.number)
 | |
|             friend.set_alias('')
 | |
|             try:
 | |
|                 index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
 | |
|                 del aliases[index]
 | |
|             except:
 | |
|                 pass
 | |
|         self._settings.save()
 | |
| 
 | |
|     def friend_public_key(self, num):
 | |
|         return self._contacts[num].tox_id
 | |
| 
 | |
|     def delete_friend(self, num):
 | |
|         """
 | |
|         Removes friend from contact list
 | |
|         :param num: number of friend in list
 | |
|         """
 | |
|         friend = self._contacts[num]
 | |
|         self._cleanup_contact_data(friend)
 | |
|         try:
 | |
|             self._tox.friend_delete(friend.number)
 | |
|         except Exception as e:
 | |
|             LOG.warn(f"'There was no friend with the given friend number {e}")
 | |
|         self._delete_contact(num)
 | |
| 
 | |
|     def add_friend(self, tox_id):
 | |
|         """
 | |
|         Adds friend to list
 | |
|         """
 | |
|         self._tox.friend_add_norequest(tox_id)
 | |
|         self._add_friend(tox_id)
 | |
|         self.update_filtration()
 | |
| 
 | |
|     def block_user(self, tox_id):
 | |
|         """
 | |
|         Block user with specified tox id (or public key) - delete from friends list and ignore friend requests
 | |
|         """
 | |
|         tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
 | |
|         if tox_id == self._tox.self_get_address()[:TOX_PUBLIC_KEY_SIZE * 2]:
 | |
|             return
 | |
|         if tox_id not in self._settings['blocked']:
 | |
|             self._settings['blocked'].append(tox_id)
 | |
|             self._settings.save()
 | |
|         try:
 | |
|             num = self._tox.friend_by_public_key(tox_id)
 | |
|             self.delete_friend(num)
 | |
|             self.save_profile()
 | |
|         except:  # not in friend list
 | |
|             pass
 | |
| 
 | |
|     def unblock_user(self, tox_id, add_to_friend_list):
 | |
|         """
 | |
|         Unblock user
 | |
|         :param tox_id: tox id of contact
 | |
|         :param add_to_friend_list: add this contact to friend list or not
 | |
|         """
 | |
|         self._settings['blocked'].remove(tox_id)
 | |
|         self._settings.save()
 | |
|         if add_to_friend_list:
 | |
|             self.add_friend(tox_id)
 | |
|             self.save_profile()
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Groups support
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def get_group_chats(self):
 | |
|         return list(filter(lambda c: type(c) is GroupChat, self._contacts))
 | |
| 
 | |
|     def add_group(self, group_number):
 | |
|         index = len(self._contacts)
 | |
|         group = self._contact_provider.get_group_by_number(group_number)
 | |
|         # group num >= 0?
 | |
|         if group is None:
 | |
|             LOG.warn(f"CM.add_group: NO group {group_number}")
 | |
|         else:
 | |
|             LOG.info(f"CM.add_group: Adding group {group._name}")
 | |
|             self._contacts.append(group)
 | |
|             LOG.info(f"contacts_manager.add_group: saving profile")
 | |
|             self._save_profile()
 | |
|             group.reset_avatar(self._settings['identicons'])
 | |
|             LOG.info(f"contacts_manager.add_group: setting active")
 | |
|             self.set_active(index)
 | |
|             self.update_filtration()
 | |
| 
 | |
|     def delete_group(self, group_number):
 | |
|         group = self.get_group_by_number(group_number)
 | |
|         self._cleanup_contact_data(group)
 | |
|         num = self._contacts.index(group)
 | |
|         self._delete_contact(num)
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Groups private messaging
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def add_group_peer(self, group, peer):
 | |
|         contact = self._contact_provider.get_group_peer_by_id(group, peer.id)
 | |
|         if self.check_if_contact_exists(contact.tox_id):
 | |
|             return
 | |
|         contact._kind = 'grouppeer'
 | |
|         self._contacts.append(contact)
 | |
|         contact.reset_avatar(self._settings['identicons'])
 | |
|         self._save_profile()
 | |
| 
 | |
|     def remove_group_peer_by_id(self, group, peer_id):
 | |
|         peer = group.get_peer_by_id(peer_id)
 | |
|         if peer: # broken
 | |
|             if not self.check_if_contact_exists(peer.public_key):
 | |
|                 return
 | |
|             contact = self.get_contact_by_tox_id(peer.public_key)
 | |
|             self.remove_group_peer(contact)
 | |
| 
 | |
|     def remove_group_peer(self, group_peer_contact):
 | |
|         contact = self.get_contact_by_tox_id(group_peer_contact.tox_id)
 | |
|         if contact:
 | |
|             self._cleanup_contact_data(contact)
 | |
|             num = self._contacts.index(contact)
 | |
|             self._delete_contact(num)
 | |
| 
 | |
|     def get_gc_peer_name(self, name):
 | |
|         group = self.get_curr_contact()
 | |
| 
 | |
|         names = sorted(group.get_peers_names())
 | |
|         if name in names:  # return next nick
 | |
|             index = names.index(name)
 | |
|             index = (index + 1) % len(names)
 | |
| 
 | |
|             return names[index]
 | |
| 
 | |
|         suggested_names = list(filter(lambda x: x.startswith(name), names))
 | |
|         if not len(suggested_names):
 | |
|             return '\t'
 | |
| 
 | |
|         return suggested_names[0]
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Friend requests
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def send_friend_request(self, sToxPkOrId, message):
 | |
|         """
 | |
|         Function tries to send request to contact with specified id
 | |
|         :param sToxPkOrId: id of new contact or tox dns 4 value
 | |
|         :param message: additional message
 | |
|         :return: True on success else error string
 | |
|         """
 | |
|         retval = ''
 | |
|         try:
 | |
|             message = message or 'Hello! Add me to your contact list please'
 | |
|             if len(sToxPkOrId) == TOX_PUBLIC_KEY_SIZE * 2:  # public key
 | |
|                 self.add_friend(sToxPkOrId)
 | |
|                 title = 'Friend added'
 | |
|                 text = 'Friend added without sending friend request'
 | |
|             else:
 | |
|                 num = self._tox.friend_add(sToxPkOrId, message.encode('utf-8'))
 | |
|                 if num < UINT32_MAX:
 | |
|                     tox_pk = sToxPkOrId[:TOX_PUBLIC_KEY_SIZE * 2]
 | |
|                     self._add_friend(tox_pk)
 | |
|                     self.update_filtration()
 | |
|                     title = 'Friend added'
 | |
|                     text = 'Friend added by sending friend request'
 | |
|                     self.save_profile()
 | |
|                     retval = True
 | |
|                 else:
 | |
|                     title = 'Friend failed'
 | |
|                     text = 'Friend failed sending friend request'
 | |
|                     retval = text
 | |
| 
 | |
|         except Exception as ex:  # wrong data
 | |
|             title = 'Friend add exception'
 | |
|             text = 'Friend request exception with ' + str(ex)
 | |
|             self._log(text)
 | |
|             LOG.exception(text)
 | |
|             LOG.warn(f"DELETE {sToxPkOrId} ?")
 | |
|             retval = str(ex)
 | |
|         title = util_ui.tr(title)
 | |
|         text = util_ui.tr(text)
 | |
|         util_ui.message_box(text, title)
 | |
|         return retval
 | |
| 
 | |
|     def process_friend_request(self, tox_id, message):
 | |
|         """
 | |
|         Accept or ignore friend request
 | |
|         :param tox_id: tox id of contact
 | |
|         :param message: message
 | |
|         """
 | |
|         if tox_id in self._settings['blocked']:
 | |
|             return
 | |
|         try:
 | |
|             text = util_ui.tr('User {} wants to add you to contact list. Message:\n{}')
 | |
|             reply = util_ui.question(text.format(tox_id, message), util_ui.tr('Friend request'))
 | |
|             if reply:  # accepted
 | |
|                 self.add_friend(tox_id)
 | |
|                 data = self._tox.get_savedata()
 | |
|                 self._profile_manager.save_profile(data)
 | |
|         except Exception as ex:  # something is wrong
 | |
|             LOG.error('Accept friend request failed! ' + str(ex))
 | |
| 
 | |
|     def can_send_typing_notification(self):
 | |
|         return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer()
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Contacts numbers update
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def update_friends_numbers(self):
 | |
|         for friend in self._contact_provider.get_all_friends():
 | |
|             friend.number = self._tox.friend_by_public_key(friend.tox_id)
 | |
|         self.update_filtration()
 | |
| 
 | |
|     def update_groups_numbers(self):
 | |
|         groups = self._contact_provider.get_all_groups()
 | |
|         LOG.info(f"update_groups_numbers len(groups)={len(groups)}")
 | |
|         # Thread 76 "ToxIterateThrea" received signal SIGSEGV, Segmentation fault.
 | |
|         for i in range(len(groups)):
 | |
|             chat_id = self._tox.group_get_chat_id(i)
 | |
|             if not chat_id:
 | |
|                 LOG.warn(f"update_groups_numbers {i} chat_id")
 | |
|                 continue
 | |
|             group = self.get_contact_by_tox_id(chat_id)
 | |
|             if not group:
 | |
|                 LOG.warn(f"update_groups_numbers {i} group")
 | |
|                 continue
 | |
|             group.number = i
 | |
|         self.update_filtration()
 | |
| 
 | |
|     def update_groups_lists(self):
 | |
|         groups = self._contact_provider.get_all_groups()
 | |
|         for group in groups:
 | |
|             group.remove_all_peers_except_self()
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Private methods
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def _load_contacts(self):
 | |
|         self._load_friends()
 | |
|         self._load_groups()
 | |
|         if len(self._contacts):
 | |
|             self.set_active(0)
 | |
|         # filter(lambda c: not c.has_avatar(), self._contacts)
 | |
|         for (i, contact) in enumerate(self._contacts):
 | |
|             if contact is None:
 | |
|                 LOG.warn(f"_load_contacts NULL contact {i}")
 | |
|                 LOG.info(f"_load_contacts deleting NULL {self._contacts[i]}")
 | |
|                 del self._contacts[i]
 | |
|                 #? self.save_profile()
 | |
|                 continue
 | |
|             if contact.has_avatar(): continue
 | |
|             contact.reset_avatar(self._settings['identicons'])
 | |
|         self.update_filtration()
 | |
| 
 | |
|     def _load_friends(self):
 | |
|         self._contacts.extend(self._contact_provider.get_all_friends())
 | |
| 
 | |
|     def _load_groups(self):
 | |
|         self._contacts.extend(self._contact_provider.get_all_groups())
 | |
| 
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
|     # Current contact subscriptions
 | |
|     # -----------------------------------------------------------------------------------------------------------------
 | |
| 
 | |
|     def _subscribe_to_events(self, contact):
 | |
|         contact.name_changed_event.add_callback(self._current_contact_name_changed)
 | |
|         contact.status_changed_event.add_callback(self._current_contact_status_changed)
 | |
|         contact.status_message_changed_event.add_callback(self._current_contact_status_message_changed)
 | |
|         contact.avatar_changed_event.add_callback(self._current_contact_avatar_changed)
 | |
| 
 | |
|     def _unsubscribe_from_events(self, contact):
 | |
|         contact.name_changed_event.remove_callback(self._current_contact_name_changed)
 | |
|         contact.status_changed_event.remove_callback(self._current_contact_status_changed)
 | |
|         contact.status_message_changed_event.remove_callback(self._current_contact_status_message_changed)
 | |
|         contact.avatar_changed_event.remove_callback(self._current_contact_avatar_changed)
 | |
| 
 | |
|     def _current_contact_name_changed(self, name):
 | |
|         self._screen.account_name.setText(name)
 | |
| 
 | |
|     def _current_contact_status_changed(self, status):
 | |
|         pass
 | |
| 
 | |
|     def _current_contact_status_message_changed(self, status_message):
 | |
|         self._screen.account_status.setText(status_message)
 | |
| 
 | |
|     def _current_contact_avatar_changed(self, avatar_path):
 | |
|         self._set_current_contact_avatar(avatar_path)
 | |
| 
 | |
|     def _set_current_contact_data(self, contact):
 | |
|         self._screen.account_name.setText(contact.name)
 | |
|         self._screen.account_status.setText(contact.status_message)
 | |
|         self._set_current_contact_avatar(contact.get_avatar_path())
 | |
| 
 | |
|     def _set_current_contact_avatar(self, avatar_path):
 | |
|         width = self._screen.account_avatar.width()
 | |
|         pixmap = QtGui.QPixmap(avatar_path)
 | |
|         self._screen.account_avatar.setPixmap(pixmap.scaled(width, width,
 | |
|                                                             QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
 | |
| 
 | |
|     def _add_friend(self, tox_id):
 | |
|         self._history.add_friend_to_db(tox_id)
 | |
|         friend = self._contact_provider.get_friend_by_public_key(tox_id)
 | |
|         index = len(self._contacts)
 | |
|         self._contacts.append(friend)
 | |
|         if not friend.has_avatar():
 | |
|             friend.reset_avatar(self._settings['identicons'])
 | |
|         self._save_profile()
 | |
|         self.set_active(index)
 | |
| 
 | |
|     def _save_profile(self):
 | |
|         data = self._tox.get_savedata()
 | |
|         self._profile_manager.save_profile(data)
 | |
| 
 | |
|     def _cleanup_contact_data(self, contact):
 | |
|         try:
 | |
|             index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(contact.tox_id)
 | |
|             del self._settings['friends_aliases'][index]
 | |
|         except:
 | |
|             pass
 | |
|         if contact.tox_id in self._settings['notes']:
 | |
|             del self._settings['notes'][contact.tox_id]
 | |
|         self._settings.save()
 | |
|         self._history.delete_history(contact)
 | |
|         if contact.has_avatar():
 | |
|             avatar_path = contact.get_contact_avatar_path()
 | |
|             remove(avatar_path)
 | |
| 
 | |
|     def _delete_contact(self, num):
 | |
|         self.set_active(-1 if len(self._contacts) == 1 else 0)
 | |
| 
 | |
|         self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id)
 | |
|         del self._contacts[num]
 | |
|         self._screen.friends_list.takeItem(num)
 | |
|         self._save_profile()
 | |
| 
 | |
|         self.update_filtration()
 | 
