[telepathy-ashes/master] Subscribe to our subscribers.

David Laban david.laban at collabora.co.uk
Thu Oct 8 15:00:49 PDT 2009


Special-casing contact lists so that we are always subscribed to everyone we
publish to.
---
 ashes/tools/dispatchers.py      |    1 +
 ashes/tools/echo_bot.py         |    2 +-
 ashes/tools/groups.py           |   95 ++++++++++++++++++++++++++++++++++++---
 ashes/tools/helper_functions.py |    5 ++
 ashes/tools/media_echoer.py     |   10 ++--
 ashes/tools/presence.py         |    5 ++-
 6 files changed, 104 insertions(+), 14 deletions(-)

diff --git a/ashes/tools/dispatchers.py b/ashes/tools/dispatchers.py
index 8f14f4c..24c025e 100644
--- a/ashes/tools/dispatchers.py
+++ b/ashes/tools/dispatchers.py
@@ -126,6 +126,7 @@ class ChannelDispatcher(ConnectionListener):
             handler_class = self._handler_classes[channel_type, handle_type]
             handler = handler_class(self, channel, properties)
             channels.append(handler)
+            return handler
         else:
             print "Don't know how to handle channel with handle type:", handle_type,
             print "and channel type", channel_type
diff --git a/ashes/tools/echo_bot.py b/ashes/tools/echo_bot.py
index 7a19e7e..56dbc1c 100644
--- a/ashes/tools/echo_bot.py
+++ b/ashes/tools/echo_bot.py
@@ -35,7 +35,7 @@ class EchoBot(  PresenceEchoer,
     into a nice tasty echo bot.
     """
     # Using python's name mangling to avoid name collisions.
-    __channel_handler_classes = [MediaChannelEchoer, ContactAcceptor,
+    __channel_handler_classes = [MediaChannelEchoer,
         TextChannelEchoer, FileTransferEchoer]
 
 
diff --git a/ashes/tools/groups.py b/ashes/tools/groups.py
index 622b5c4..cd37e72 100644
--- a/ashes/tools/groups.py
+++ b/ashes/tools/groups.py
@@ -2,13 +2,13 @@
 import telepathy
 
 from bases import ChannelListener
-from helper_functions import get_property, printer, green
+from dispatchers import ChannelDispatcher
+from helper_functions import get_property, get_all_properties, printer, green
 
-class ContactAcceptor(ChannelListener):
+
+class MemberAcceptor(ChannelListener):
     """Automatically accepts local_pending members of groups."""
 
-    channel_type = 'org.freedesktop.Telepathy.Channel.Type.ContactList'
-    handle_type = telepathy.HANDLE_TYPE_LIST
 
     def __init__(self, connection, channel, properties):
         ChannelListener.__init__(self, connection, channel, properties)
@@ -19,8 +19,12 @@ class ContactAcceptor(ChannelListener):
         requested = properties['org.freedesktop.Telepathy.Channel.Requested']
         self.id = properties['org.freedesktop.Telepathy.Channel.TargetID']
         print 'List Handled:', self.id
-        get_property(channel, 'org.freedesktop.Telepathy.Channel.Interface.Group',
-            'LocalPendingMembers', reply_handler=self.local_pending, error_handler=printer)
+        get_all_properties(channel,
+            "org.freedesktop.Telepathy.Channel.Interface.Group",
+            self.group_properties_callback)
+
+    def group_properties_callback(self, properties):
+        self.local_pending(properties["LocalPendingMembers"])
 
     def local_pending(self, pending):
         """
@@ -62,12 +66,14 @@ class ContactAcceptor(ChannelListener):
             print green("Allowing members to join %s:"%self.id),
             print handles_to_ids(local_pending, member_ids),
             print "at the request of:", actor
-            self.channel[telepathy.CHANNEL_INTERFACE_GROUP].AddMembers(local_pending, '')
+            self.add_members(local_pending)
         if remote_pending:
             print green("Members invited to %s:"%self.id),
             print handles_to_ids(remote_pending, member_ids),
             print "at the request of:", actor
 
+    def add_members(self, members):
+        self.channel[telepathy.CHANNEL_INTERFACE_GROUP].AddMembers(members, '')
 
 def handles_to_ids(handles, member_ids={}):
     """
@@ -82,3 +88,78 @@ def handles_to_ids(handles, member_ids={}):
         ret.append(str(id))
     return ret
 
+
+class ContactAcceptor(MemberAcceptor):
+    """
+    A special case of the groups interface which magically keeps publish and
+    subscribe in sync, with the help of ContactSubscriber listening to the
+    connection.
+    """
+    channel_type = 'org.freedesktop.Telepathy.Channel.Type.ContactList'
+    handle_type = telepathy.HANDLE_TYPE_LIST
+
+    def __init__(self, *args, **kwargs):
+        super(ContactAcceptor, self).__init__(*args, **kwargs)
+        self.slave_channels = []
+
+    def group_properties_callback(self, properties):
+        super(ContactAcceptor, self).group_properties_callback(properties)
+        members = properties["Members"]
+        for channel in self.slave_channels:
+            channel.add_members(members)
+
+    def add_slave_channel(self, channel):
+        """
+        Register a channel as a slave of this one. All members added to this
+        channel will also be added to all slave channels.
+        Please don't create loops of slave channels, as this makes me very sad.
+        """
+        self.slave_channels.append(channel)
+
+    def add_members(self, members):
+        """
+        Add members to the current channel, and also to all channels which
+        have been registered as slaves using add_slave_channel()
+        """
+        self.channel[telepathy.CHANNEL_INTERFACE_GROUP].AddMembers(members, '')
+        for channel in self.slave_channels:
+            channel.add_members(members)
+
+class ContactSubscriber(ChannelDispatcher):
+    """
+    This connection listener accepts all contacts who subscribe to us.
+    It isn't always as simple as that though.
+    Anyone we publish our presence to, we should also subscribe to presence
+    from. This is a requirement for getting PresenceEchoer to work.
+    In most cases, someone adding us to their contact list
+    """
+    __channel_handler_classes = [ContactAcceptor]
+
+    def __init__(self, *args, **kwargs):
+        super(ContactSubscriber, self).__init__(*args, **kwargs)
+        self.contact_list_handlers = {}
+
+    def dispatch_channel(self, channel, properties):
+        """
+        Adds a special case to ContactAcceptor classes (which handle magic
+        ContactList groups like publish and subscribe).
+        """
+        handler = super(ContactSubscriber, self).dispatch_channel(channel, properties)
+        if isinstance(handler, ContactAcceptor):
+            self.register_contact_list_handler(handler)
+        return handler
+
+    def register_contact_list_handler(self,handler):
+        """
+        If the contact lists "publish" and "subscribe" are both registered
+        then copy contacts from publish to subscribe to keep them in sync.
+        """
+        self.contact_list_handlers[handler.id] = handler
+        if handler.id in ["publish", "subscribe"] and \
+                "publish" in self.contact_list_handlers and \
+                "subscribe" in self.contact_list_handlers:
+            subscribe_list = self.contact_list_handlers["subscribe"]
+            publish_list = self.contact_list_handlers["publish"]
+            publish_list.add_slave_channel(subscribe_list)
+
+
diff --git a/ashes/tools/helper_functions.py b/ashes/tools/helper_functions.py
index 8fc0827..778e94e 100644
--- a/ashes/tools/helper_functions.py
+++ b/ashes/tools/helper_functions.py
@@ -26,6 +26,11 @@ def get_property(object, interface, name, **kwargs):
     """Use the Properties interface to get a property from an interface."""
     return object['org.freedesktop.DBus.Properties'].Get(interface, name, **kwargs)
 
+def get_all_properties(object, interface, callback):
+    return object['org.freedesktop.DBus.Properties'].GetAll(interface,
+                reply_handler=callback,
+                error_handler=error_printer("Get all properties on %s" % interface))
+
 def get_connections(bus=None, *args, **kwargs):
     """Like telepathy.client.conn.Connection.get_connections, but allows 
     forwarding of arguments to Connection()."""
diff --git a/ashes/tools/media_echoer.py b/ashes/tools/media_echoer.py
index 2187e0c..39a9741 100644
--- a/ashes/tools/media_echoer.py
+++ b/ashes/tools/media_echoer.py
@@ -13,13 +13,13 @@ import gst
 import telepathy
 
 from bases import ChannelListener, ObjectListener
-from groups import ContactAcceptor
+from groups import MemberAcceptor
 from helper_functions import (get_connections, get_property,
         green, red, printer, rpartial)
 
     
 
-class MediaChannelEchoer(ContactAcceptor):
+class MediaChannelEchoer(MemberAcceptor):
     """
     Listens to a media channel and echoes everything it hears/sees.
     # NOTE: this isn't currently true: it currently just uses testsrc.
@@ -30,7 +30,7 @@ class MediaChannelEchoer(ContactAcceptor):
     capabilities_flag = 0xff
 
     def __init__(self, connection, channel, properties):
-        ContactAcceptor.__init__(self, connection, channel, properties)
+        MemberAcceptor.__init__(self, connection, channel, properties)
         print green('Media Channel Handled.')
         self.tfchannel = tpfarsight.Channel(self.connection.service_name,
                                             self.connection.object_path,
@@ -133,7 +133,7 @@ class MediaChannelEchoer(ContactAcceptor):
 
 
 
-class IceEchoer(ContactAcceptor):
+class IceEchoer(MemberAcceptor):
     """
     Avoids using farsight, and just does hackery with signals to make remote
     clients talk to themselves.
@@ -141,7 +141,7 @@ class IceEchoer(ContactAcceptor):
     def __init__(self, connection, channel, properties):
         self.session_handlers = []
         self.stream_handlers = {}
-        ContactAcceptor.__init__(self, connection, channel, properties)
+        MemberAcceptor.__init__(self, connection, channel, properties)
         print green('Media Channel Handled.')
         channel["org.freedesktop.Telepathy.Channel.Interface.MediaSignalling"
             ].GetSessionHandlers(reply_handler=self.NewSessionHandlers,
diff --git a/ashes/tools/presence.py b/ashes/tools/presence.py
index 206d989..90db60b 100644
--- a/ashes/tools/presence.py
+++ b/ashes/tools/presence.py
@@ -6,12 +6,15 @@ import telepathy
 from telepathy.constants import HANDLE_TYPE_CONTACT
 
 from bases import ConnectionListener
+from groups import ContactSubscriber
 from helper_functions import printer
 
 class Onlineifier(ConnectionListener):
     """
     Sets presence to online on setup
     """
+    SIMPLE_PRESENCE = telepathy.interfaces.CONNECTION_INTERFACE_SIMPLE_PRESENCE
+    PRESENCE = telepathy.interfaces.CONNECTION_INTERFACE_PRESENCE
     def finish_setup(self, *_args):
         super(Onlineifier, self).finish_setup(*_args)
         print "Telling connection to connect."
@@ -28,7 +31,7 @@ class Onlineifier(ConnectionListener):
         super(Onlineifier, self).StatusChanged(status, reason)
 
 
-class PresenceEchoer(ConnectionListener):
+class PresenceEchoer(ContactSubscriber):
     """Extends ConnectionListener to echo Presence.PresenceUpdate and
     SimplePresence.PresencesChanged."""
     SIMPLE_PRESENCE = telepathy.interfaces.CONNECTION_INTERFACE_SIMPLE_PRESENCE
-- 
1.5.6.5




More information about the telepathy-commits mailing list