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,
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)
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'))
return self._backend
@property
+ def addressbook(self):
+ return self._addressbook
+
+ @property
def username(self):
self._credentials[0]
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)
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)
import logging
+import util.coroutines as coroutines
+
_moduleLogger = logging.getLogger("gvoice.addressbook")
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)