[telepathy-ashes/master] added ability to connect using a .account file. Streamed media still doesn't work.

David Laban (none) dl325 at eee
Thu Sep 17 15:34:04 PDT 2009


---
 ashes/tools/account.py  |   57 ++++++++++++++++++
 ashes/tools/echo_bot.py |  152 ++++++++++++++++++++++++++++++++---------------
 2 files changed, 160 insertions(+), 49 deletions(-)
 create mode 100644 ashes/tools/account.py

diff --git a/ashes/tools/account.py b/ashes/tools/account.py
new file mode 100644
index 0000000..bb9172c
--- /dev/null
+++ b/ashes/tools/account.py
@@ -0,0 +1,57 @@
+import telepathy
+from telepathy.interfaces import CONN_MGR_INTERFACE
+import dbus
+
+def parse_account(s):
+    lines = s.splitlines()
+    pairs = []
+    manager = None
+    protocol = None
+
+    for line in lines:
+        if not line.strip():
+            continue
+
+        k, v = line.split(':', 1)
+        k = k.strip()
+        v = v.strip()
+
+        if k == 'manager':
+            manager = v
+        elif k == 'protocol':
+            protocol = v
+        else:
+            if k not in ("account", "password"):
+                if v.lower() == "false":
+                    v = False
+                elif v.lower() == "true":
+                    v = True
+                else:
+                    try:
+                        v = dbus.UInt32(int(v))
+                    except:
+                        pass
+            pairs.append((k, v))
+
+    assert manager
+    assert protocol
+    return manager, protocol, dict(pairs)
+
+def read_account(path):
+    return parse_account(file(path).read())
+
+def connect(manager, protocol, account, ready_handler=None):
+    reg = telepathy.client.ManagerRegistry()
+    reg.LoadManagers()
+
+    mgr = reg.GetManager(manager)
+    conn_bus_name, conn_object_path = \
+        mgr[CONN_MGR_INTERFACE].RequestConnection(protocol, account)
+    return telepathy.client.Connection(conn_bus_name, conn_object_path,
+        ready_handler=ready_handler)
+
+def connection_from_file(path, ready_handler=None):
+    manager, protocol, account = read_account(path)
+    return connect(manager, protocol, account, ready_handler=ready_handler)
+
+
diff --git a/ashes/tools/echo_bot.py b/ashes/tools/echo_bot.py
index c78738c..608ef7d 100644
--- a/ashes/tools/echo_bot.py
+++ b/ashes/tools/echo_bot.py
@@ -1,7 +1,7 @@
 import re
 import time
 import pdb
-import shiny
+import sys
 
 import gobject
 import gst
@@ -9,10 +9,14 @@ import dbus
 import dbus.mainloop.glib
 import telepathy
 from telepathy.constants import HANDLE_TYPE_CONTACT
+sys.path.append('/usr/lib/python2.6/site-packages')
 import tpfarsight
 import farsight
 
+
+import shiny
 from helper_functions import get_connections, green, red
+from account import connection_from_file
 
 #TODO: wrap everything to 80 chars so that I can hack on my eeepc.
 
@@ -37,6 +41,10 @@ class ObjectListener(object):
             return True
         else:
             return False
+    
+    def __getitem__(self, iface):
+        """Enables InterfaceFactory gumph."""
+        return self.dbus_object[iface]
             
     
     def connect_to_signal(self, iface_name, signal_name):
@@ -74,21 +82,34 @@ class ConnectionListener(ObjectListener):
     can be done reliably using the __ prefix for members, and only writing one
     signal handler per signal. If common functionality is needed in sister 
     classes, push it up to the parent class.
+    
+    WARNING: connections are not guaranteed to be ready when this is initialised.
     """
     CONNECTION = telepathy.interfaces.CONNECTION
     def __init__(self, connection, contact_regexp='.*'):
         """
-        contact_regexp is used in base classes to selectively echo only specific users.
+        contact_regexp is used in derived classes to selectively echo only specific users.
         """
+        self.init(connection, contact_regexp)
+        
+    def init(self, connection, contact_regexp='.*'):
+        print "Listening to connection"
         ObjectListener.__init__(self, connection)
-        # I could use self.dbus_object, but connection everywhere, but 
+        # I could use self.dbus_object everywhere, but 
         # self.connection is clearer.
         self.connection = connection 
         self.contact_regexp = contact_regexp
         self.channel_extra_args = [contact_regexp]
-        connection.call_when_ready(self._register_callbacks)
-        
+        self.connection.Connect()
+        connection.call_when_ready(self.finish_setup)
 
+    def finish_setup(self, *args):
+        """
+        Calls register_callbacks and ensures that the connection is connected.
+        """
+        print 'finishing setup'
+        self._register_callbacks(self.connection)
+        self.connection.Connect()
     def _register_callbacks(self, conn):
         """
         Registers callbacks for all signals provided by the connection. 
@@ -135,7 +156,16 @@ class ConnectionListener(ObjectListener):
         'org.freedesktop.DBus.Properties':
             [],
         }
-
+class Onlineifier(ConnectionListener):
+    """
+    Sets presence to online on setup
+    """
+    def finish_setup(self, *_args):
+        print "Setting status to online"
+        super(Onlineifier, self).finish_setup(*_args)
+        self.connection.Connect()
+        self.connection[self.SIMPLE_PRESENCE].SetPresence('available', '')
+        
 class ChannelDispatcher(ConnectionListener):
     """
     Listens for Requests.NewChannels and Connection.NewChannel, creates a 
@@ -170,11 +200,13 @@ class ChannelDispatcher(ConnectionListener):
             return class_
         return decorate
     
-    def __init__(self, connection, contact_regexp='.*'):
-        # FIXME: This should really be done only if there is a handler for this type.
-        # It should also probably be done in some kind of setup function rather than 
-        # __init__ because I don't know what state the connection is in here.
-        super(ChannelDispatcher, self).__init__(connection, contact_regexp)
+    def finish_setup(self, *conn):
+        """
+        Sets up super-classes recursively, then sets capabilities
+        FIXME: do this based on _handler_classes rather than hardcoded.
+        """
+        print "advertising streamed media caps"
+        super(ChannelDispatcher, self).finish_setup()
         self.connection[telepathy.CONN_INTERFACE_CAPABILITIES].AdvertiseCapabilities(
             [(telepathy.CHANNEL_TYPE_STREAMED_MEDIA, 0xff)], [])
     
@@ -218,10 +250,12 @@ class ChannelDispatcher(ConnectionListener):
         
         if (channel_type, handle_type) in self._handler_classes:
             handler_class = self._handler_classes[channel_type, handle_type]
-            handler = handler_class(self, channel, properties, *self.channel_extra_args)
+            handler = handler_class(self, channel, properties)
             channels.append(handler)
         else:
-            print "Don't know how to handle channel with handle type:", handle_type
+            print "Don't know how to handle channel with handle type:", handle_type,
+            print "and channel type", channel_type 
+
 
     
 class PresenceEchoer(ConnectionListener):
@@ -247,14 +281,14 @@ class PresenceEchoer(ConnectionListener):
         """Handles SimplePresence.PresencesChanged, and echoes it."""
         #FIXME: This may cause a flood on connect. Should probably put a limit here.
         for handle, presence in presences.items():
-            
-            (contact_string,) = self.connection.InspectHandles(HANDLE_TYPE_CONTACT, [handle])
+            type, status, message = presence
             self_handle = self._get_self_handle()
-            if (handle != self_handle and 
-                                re.match(self.contact_regexp, contact_string)):
-                type, status, message = presence
-                new_message = self.identify(contact_string, message)
-                self.connection[self.SIMPLE_PRESENCE].SetPresence(status, new_message)
+            if (type > 1 and handle != self_handle):
+                (contact_string,) = self.connection.InspectHandles(HANDLE_TYPE_CONTACT, [handle])
+
+                if re.match(self.contact_regexp, contact_string):
+                    new_message = self.identify(contact_string, message)
+                    self.connection[self.SIMPLE_PRESENCE].SetPresence(status, new_message)
     
     def PresenceUpdate(self, presences):
         """
@@ -266,18 +300,22 @@ class PresenceEchoer(ConnectionListener):
         #FIXME: This may cause a flood on connect. Should probably put a limit here.
         
         for handle, presence in presences.items():
+            last_activity, statuses = presence
+            ((status, parameters),) = statuses.items()
             
-            (contact_string,) = self.connection.InspectHandles(HANDLE_TYPE_CONTACT, [handle])
             self_handle = self._get_self_handle()
-            if (handle != self_handle and 
-                                re.match(self.contact_regexp, contact_string)):
-                last_activity, statuses = presence
-                ((status, parameters),) = statuses.items()
-                message = parameters.get('message', '')
-                new_message = self.identify(contact_string, message)
-                self.connection[self.PRESENCE].SetStatus({status: {'message': new_message}})
-
-class EchoBot(PresenceEchoer, ChannelDispatcher):
+            if (handle != self_handle and status != 'offline'):
+                (contact_string,) = self.connection.InspectHandles(HANDLE_TYPE_CONTACT, [handle])
+                if re.match(self.contact_regexp, contact_string):
+                    message = parameters.get('message', '')
+                    new_message = self.identify(contact_string, message)
+                    self.connection[self.PRESENCE].SetStatus({status: {'message': new_message}})
+
+ at shiny.debug_class
+class EchoBot(  PresenceEchoer, 
+                ChannelDispatcher,
+                Onlineifier,
+                ):
     """
     This class just mixes together functionality from PresenceEchoer and friends 
     into a nice tasty echo bot.
@@ -305,6 +343,8 @@ class ChannelListener(ObjectListener):
             'StreamRemoved', 'StreamStateChanged',],
         'org.freedesktop.Telepathy.Channel.Interface.Destroyable':
             [],
+        'org.freedesktop.Telepathy.Channel.Type.ContactList':
+            [],
         'org.freedesktop.Telepathy.Channel.Interface.Messages':
             ['MessageSent', 'PendingMessagesRemoved', 'MessageReceived'],
         'org.freedesktop.Telepathy.Channel':
@@ -349,6 +389,10 @@ class ChannelListener(ObjectListener):
         
 
 
+ at ChannelDispatcher.handler_for('org.freedesktop.Telepathy.Channel.Type.ContactList', 
+                                telepathy.HANDLE_TYPE_LIST)
+class ContactAcceptor(ChannelListener):
+    pass
 
 @ChannelDispatcher.handler_for('org.freedesktop.Telepathy.Channel.Type.Text', 
                                 telepathy.HANDLE_TYPE_CONTACT)
@@ -356,7 +400,7 @@ class TextChannelEchoer(ChannelListener):
     """
     Listens to a text channel and echoes everything with id and timestamp etc.
     """
-    def __init__(self, connection, channel, properties, contact_regexp):
+    def __init__(self, connection, channel, properties):
         ChannelListener.__init__(self, connection, channel, properties)
         self.contact_string = properties['org.freedesktop.Telepathy.Channel.TargetID']
         print green('Text Channel Handled.')
@@ -383,7 +427,7 @@ class MediaChannelEchoer(ChannelListener):
     """
     Listens to a media channel and echoes everything it hears/sees.
     """
-    def __init__(self, connection, channel, properties, contact_regexp):
+    def __init__(self, connection, channel, properties):
         ChannelListener.__init__(self, connection, channel, properties)
         print green('Media Channel Handled.')
         self.tfchannel = tpfarsight.Channel(self.connection.service_name,
@@ -515,6 +559,7 @@ channels = []
 def run_echo_service(conn, contact_regexp):
     """
     Runs the echo service unless it's already running.
+    Also helpfully appends it to connections and returns that.
     """
     if conn in connections:
         #note: requires patch to dbus-python which implements __eq__
@@ -524,22 +569,14 @@ def run_echo_service(conn, contact_regexp):
         bot = EchoBot(conn, contact_regexp)
         assert bot == conn # so that 'if conn in connections:' works.
         connections.append(bot)
-    
-def connection_callback_factory(connection_regexp, contact_regexp):
-    """
-    Creates callbacks for passing into telepathy.client.Connection as 
-    ready_handler. Generated callbacks only handle connections matching
-    connection_regexp. The resulting echo service only echoes events caused by
-    contacts with InspectHandles() matching contact_regexp.
-    """
-    def connection_callback(conn):
-        if re.match(connection_regexp, conn.service_name):
-            run_echo_service(conn, contact_regexp)
-        else:
-            print conn.service_name, "does not match", connection_regexp
-    return connection_callback
+    return connections
+
+
+
 
-def main(connection_regexp='.*echo123.*', contact_regexp='.*'):
+def main(connection_regexp='.*echo123.*', 
+        contact_regexp='.*',
+        account_file=''):
     """
     This program will look on the bus for a connection object which matches 
     connection_regexp.
@@ -549,8 +586,19 @@ def main(connection_regexp='.*echo123.*', contact_regexp='.*'):
     dmainloop = dbus.mainloop.glib.DBusGMainLoop()
     dbus.set_default_main_loop(dmainloop)
     regexp = re.compile(connection_regexp)
-    cb = connection_callback_factory(connection_regexp, contact_regexp)
-    get_connections(ready_handler=cb)
+    
+    if account_file:
+        print 'Creating connection'
+        connection = connection_from_file(account_file)
+        run_echo_service(connection, contact_regexp)
+    else:
+        connections_ = get_connections()
+    
+        for connection in connections_:
+            if re.match(connection_regexp, connection.service_name):
+                run_echo_service(connection, contact_regexp)
+            else:
+                print conn.service_name, "does not match", connection_regexp
     
     while mainloop.is_running():
         print 'running'
@@ -558,6 +606,12 @@ def main(connection_regexp='.*echo123.*', contact_regexp='.*'):
             mainloop.run()
         except KeyboardInterrupt:
             gobject.idle_add(quit)
+            if account_file:
+                # If we set it up we should probably take it down.
+                assert connections
+                assert len(connections) == 1
+                connections[0][telepathy.CONN_INTERFACE].Disconnect()
+                account_file = None
         print 'done\n\n'
         
 
-- 
1.5.6.5




More information about the telepathy-commits mailing list