[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