From 5a9e15edc57b063b41b40aa9f4143713ccae9bc1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 29 Sep 2009 21:59:58 -0500 Subject: [PATCH] Continued work on getting contacts ready to use --- src/channel/contact_list.py | 200 +++++++++++++------------------------------ src/connection.py | 20 +++-- src/gvoice/addressbook.py | 56 ++++++++++-- src/handle.py | 18 +--- 4 files changed, 120 insertions(+), 174 deletions(-) diff --git a/src/channel/contact_list.py b/src/channel/contact_list.py index 0d20dc0..3fbd722 100644 --- a/src/channel/contact_list.py +++ b/src/channel/contact_list.py @@ -3,28 +3,14 @@ import logging import telepathy +import util.go_utils as gobject_utils +import util.coroutines as coroutines import handle _moduleLogger = logging.getLogger("channel.contact_list") -def create_contact_list_channel(connection, h): - if h.get_name() == 'subscribe': - channel_class = SubscribeListChannel - elif h.get_name() == 'publish': - channel_class = PublishListChannel - elif h.get_name() == 'hide': - _moduleLogger.warn("Unsuported type %s" % h.get_name()) - elif h.get_name() == 'allow': - _moduleLogger.warn("Unsuported type %s" % h.get_name()) - elif h.get_name() == 'deny': - _moduleLogger.warn("Unsuported type %s" % h.get_name()) - else: - raise TypeError("Unknown list type : " + h.get_name()) - return channel_class(connection, h) - - class AbstractListChannel( telepathy.server.ChannelTypeContactList, telepathy.server.ChannelInterfaceGroup, @@ -36,141 +22,71 @@ class AbstractListChannel( telepathy.server.ChannelInterfaceGroup.__init__(self) self._conn_ref = weakref.ref(connection) + self._addessbook = connection.addressbook - def GetLocalPendingMembersWithInfo(self): - return [] - -class SubscribeListChannel(AbstractListChannel): +class AllContactsListChannel(AbstractListChannel): """ - Subscribe List channel. - - This channel contains the list of contact to whom the current used is - 'subscribed', basically this list contains the contact for whom you are - supposed to receive presence notification. + The group of contacts for whom you receive presence """ def __init__(self, connection, h): AbstractListChannel.__init__(self, connection, h) - self.GroupFlagsChanged( - telepathy.CHANNEL_GROUP_FLAG_CAN_ADD | - telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0, + self._addressbook.updateSignalHandle.register_sink( + self._on_contacts_refreshed ) - def AddMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for h in contacts: - h = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h) - contact = h.contact - if contact is None: - account = h.account - else: - account = contact.account - groups = list(h.pending_groups) - h.pending_groups = set() - addressBook.add_messenger_contact(account, - invite_message=message.encode('utf-8'), - groups=groups) - - def RemoveMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for h in contacts: - h = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h) - contact = h.contact - addressBook.delete_contact(contact) - - -class PublishListChannel(AbstractListChannel): + @coroutines.func_sink + @coroutines.expand_positional + @gobject_utils.async + def _on_contacts_refreshed(self, addressbook, added, removed, changed): + connection = self._conn_ref() + handlesAdded = [ + handle.create_handle(connection, "contact", contactId) + for contactId in added + ] + handlesRemoved = [ + handle.create_handle(connection, "contact", contactId) + for contactId in removed + ] + message = "" + actor = 0 + reason = telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE + self.MembersChanged( + message, + handlesAdded, handlesRemoved, + (), (), + actor, + reason, + ) - def __init__(self, connection, h): - AbstractListChannel.__init__(self, connection, h) - self.GroupFlagsChanged(0, 0) - - def AddMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, - contactHandleId) - contact = contactHandle.contact - addressBook.accept_contact_invitation(contact, False) - - def RemoveMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, - contactHandleId) - contact = contactHandle.contact - - def GetLocalPendingMembersWithInfo(self): - addressBook = self._conn.gvoice_client - result = [] - for contact in addressBook.contacts: - h = handle.create_handle(self._conn_ref(), 'contact', - contact.account, contact.network_id) - result.append((h, h, - telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED, - contact.attributes.get('invite_message', ''))) - return result - - -class GroupChannel(AbstractListChannel): - def __init__(self, connection, h): - self.__pending_add = [] - self.__pending_remove = [] - AbstractListChannel.__init__(self, connection, h) - self.GroupFlagsChanged( - telepathy.CHANNEL_GROUP_FLAG_CAN_ADD | telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0, - ) +def create_contact_list_channel(connection, h): + if h.get_name() == 'subscribe': + # The group of contacts for whom you receive presence + ChannelClass = AllContactsListChannel + elif h.get_name() == 'publish': + # The group of contacts who may receive your presence + ChannelClass = AllContactsListChannel + elif h.get_name() == 'hide': + # A group of contacts who are on the publish list but are temporarily + # disallowed from receiving your presence + # This doesn't make sense to support + _moduleLogger.warn("Unsuported type %s" % h.get_name()) + elif h.get_name() == 'allow': + # A group of contacts who may send you messages + # @todo This would be cool to support + _moduleLogger.warn("Unsuported type %s" % h.get_name()) + elif h.get_name() == 'deny': + # A group of contacts who may not send you messages + # @todo This would be cool to support + _moduleLogger.warn("Unsuported type %s" % h.get_name()) + elif h.get_name() == 'stored': + # On protocols where the user's contacts are stored, this contact list + # contains all stored contacts regardless of subscription status. + ChannelClass = AllContactsListChannel + else: + raise TypeError("Unknown list type : " + h.get_name()) + return ChannelClass(connection, h) + - def AddMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - if self._handle.group is None: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - _moduleLogger.info("Adding contact %r to pending group %r" % (contactHandle, self._handle)) - if contactHandleId in self.__pending_remove: - self.__pending_remove.remove(contactHandleId) - else: - self.__pending_add.append(contactHandleId) - return - else: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - _moduleLogger.info("Adding contact %r to group %r" % (contactHandle, self._handle)) - contact = contactHandle.contact - group = self._handle.group - if contact is not None: - addressBook.add_contact_to_group(group, contact) - else: - contactHandle.pending_groups.add(group) - - def RemoveMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - if self._handle.group is None: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - _moduleLogger.info("Adding contact %r to pending group %r" % (contactHandle, self._handle)) - if contactHandleId in self.__pending_add: - self.__pending_add.remove(contactHandleId) - else: - self.__pending_remove.append(contactHandleId) - return - else: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - _moduleLogger.info("Removing contact %r from pending group %r" % (contactHandle, self._handle)) - contact = contactHandle.contact - group = self._handle.group - if contact is not None: - addressBook.delete_contact_from_group(group, contact) - else: - contactHandle.pending_groups.discard(group) - - def Close(self): - _moduleLogger.debug("Deleting group %s" % self._handle.name) - addressBook = self._conn.gvoice_client - group = self._handle.group - addressBook.delete_group(group) diff --git a/src/connection.py b/src/connection.py index da96e44..2fd3632 100644 --- a/src/connection.py +++ b/src/connection.py @@ -47,6 +47,7 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr cookieFilePath = "%s/cookies.txt" % constants._data_path_ self._backend = gvoice.dialer.GVDialer(cookieFilePath) + self._addressbook = gvoice.addressbook.Addressbook(self._backend) self.set_self_handle(handle.create_handle(self, 'connection')) @@ -64,6 +65,10 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr return self._backend @property + def addressbook(self): + return self._addressbook + + @property def username(self): self._credentials[0] @@ -155,9 +160,8 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr if handleType == telepathy.HANDLE_TYPE_CONTACT: h = self._create_contact_handle(name) elif handleType == telepathy.HANDLE_TYPE_LIST: + # Support only server side (immutable) lists h = handle.create_handle(self, 'list', name) - elif handleType == telepathy.HANDLE_TYPE_GROUP: - h = handle.create_handle(self, 'group', name) else: raise telepathy.NotAvailable('Handle type unsupported %d' % handleType) handles.append(h.id) @@ -167,16 +171,16 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr def _create_contact_handle(self, name): requestedContactId = name - contacts = self._backend.get_contacts() + contacts = self._addressbook.get_contacts() contactsFound = [ - (contactId, contactName) for (contactId, contactName) in contacts + contactId for contactId in contacts if contactId == requestedContactId ] if 0 < len(contactsFound): - contactId, contactName = contactsFound[0] + contactId = contactsFound[0] if len(contactsFound) != 1: - _moduleLogger.error("Contact ID was not unique: %s for %s" % (contactId, contactName)) + _moduleLogger.error("Contact ID was not unique: %s for %s" % (contactId, )) else: - contactId, contactName = requestedContactId, "" - h = handle.create_handle(self, 'contact', contactId, contactName) + contactId = requestedContactId + h = handle.create_handle(self, 'contact', contactId) diff --git a/src/gvoice/addressbook.py b/src/gvoice/addressbook.py index e796583..aeb8de5 100644 --- a/src/gvoice/addressbook.py +++ b/src/gvoice/addressbook.py @@ -3,6 +3,8 @@ import logging +import util.coroutines as coroutines + _moduleLogger = logging.getLogger("gvoice.addressbook") @@ -12,27 +14,65 @@ class Addressbook(object): def __init__(self, backend): self._backend = backend self._contacts = {} + self._addedContacts = set() + self._removedContacts = set() + self._changedContacts = set() - def clear_cache(self): - self._contacts.clear() + self.updateSignalHandler = coroutines.CoTee() - def get_contacts(self): + def update(self): + oldContacts = self._contacts + oldContactIds = set(self.get_contacts()) + + self._contacts = {} self._populate_contacts() + newContactIds = set(self.get_contacts()) + + self._addedContacts = newContactIds - oldContactIds + self._removedContacts = oldContactIds - newContactIds + self._changedContacts = set( + contactId + for contactId in newContactIds.intersection(oldContactIds) + if self._has_contact_changed(contactId, oldContacts) + ) + + message = self, self._addedContacts, self._removedContacts, self._changedContacts + self.updateSignalHandler.send(message) + + def get_contacts(self): return self._contacts.iterkeys() + def get_contact_name(self, contactId): + return self._contacts[contactId][0] + def get_contact_details(self, contactId): - self._populate_contacts() self._populate_contact_details(contactId) - return self._contacts[contactId] + return self._get_contact_details(contactId) def _populate_contacts(self): if self._contacts: return contacts = self._backend.get_contacts() for contactId, contactName in contacts: - self._contacts[contactId] = None + self._contacts[contactId] = (contactName, {}) def _populate_contact_details(self, contactId): - if self._contacts[contactId] is not None: + if self._get_contact_details(contactId): return - self._contacts[contactId] = self._backend.get_contact_details(contactId) + self._get_contact_details(contactId).update( + self._backend.get_contact_details(contactId) + ) + + def _get_contact_details(self, contactId): + return self._contacts[contactId][1] + + def _has_contact_changed(self, contactId, oldContacts): + oldContact = oldContacts[contactId] + oldContactName = oldContact[0] + oldContactDetails = oldContact[1] + if oldContactName != self.get_contact_name(contactId): + return True + if not oldContactDetails[1]: + return False + # if its already in the old cache, purposefully add it into the new cache + return oldContactDetails != self.get_contact_details(contactId) diff --git a/src/handle.py b/src/handle.py index 702e35a..057ddf3 100644 --- a/src/handle.py +++ b/src/handle.py @@ -66,12 +66,11 @@ class ConnectionHandle(TheOneRingHandle): class ContactHandle(TheOneRingHandle): - def __init__(self, connection, id, contactId, contactAccount): + def __init__(self, connection, id, contactId): handleType = telepathy.HANDLE_TYPE_CONTACT handleName = contactId TheOneRingHandle.__init__(self, connection, id, handleType, handleName) - self._account = contactAccount self._id = contactId @property @@ -79,12 +78,8 @@ class ContactHandle(TheOneRingHandle): return self._id @property - def contactName(self): - return self._account - - @property def contactDetails(self): - return self._conn.gvoice_client.get_contact_details(self._id) + return self._conn.addressbook.get_contact_details(self._id) class ListHandle(TheOneRingHandle): @@ -95,19 +90,10 @@ class ListHandle(TheOneRingHandle): TheOneRingHandle.__init__(self, connection, id, handleType, handleName) -class GroupHandle(TheOneRingHandle): - - def __init__(self, connection, id, groupName): - handleType = telepathy.HANDLE_TYPE_GROUP - handleName = groupName - TheOneRingHandle.__init__(self, connection, id, handleType, handleName) - - _HANDLE_TYPE_MAPPING = { 'connection': ConnectionHandle, 'contact': ContactHandle, 'list': ListHandle, - 'group': GroupHandle, } -- 1.7.9.5