Introduction
This howto describes how to use one-to-one D-tubes from an application. It is intended to be the simplest possible but nevertheless complete and usable now; thus, for instance, it does not ask the user to supply account information that it can (and should) obtain from elsewhere (such as mission control) and it uses current interfaces even where newer, better interfaces are coming.
Target audience
Developers who wish to use Telepathy Tubes as a black-box library.
Programming language
The howto is oriented toward use in python; if you wish to use C, simply treat the code as pseudo-code.
Note: currently, QtDBus cannot use peer-to-peer connections, so generally Qt users cannot use d-tubes.
One-to-one vs multi-user tubes; D-tubes vs stream tubes
I concentrate on one-to-one D-tubes; no doubt many of the steps are similar for multi-user tubes and/or for Stream tubes.
Present vs future
In general, this document aims to describe currently-available interfaces. However, currently it's a little ahead of what's widely deployed: it requires python-telepathy 0.15.12 and telepathy-gabble at least 0.9 (tested with 0.9.2). In contrast, Ubuntu 9.10 has python-telepathy 0.15.11 and telepathy-gabble 0.8.7
The example
The steps are embodied in an example, DTube Tutorial Example.
There's also an outdated example for sending a text message, Message Tutorial Example, which is no longer of any practical use unless updated to new versions.
Why?
Telepathy Tubes
Telepathy Tubes is a framework which allows programs to work with the IM client. Today, one can chat and send files over IM. With Tubes, other programs can extend this - the simple example would a chess program that'll let you play against one's IM buddies; the serious examples are things like shared wordprocessors or discussion whiteboards. The really important examples, of course, will be the ones we can't even imagine yet... Most importantly, from the application-developer's point of view, it takes care of two things:
identity management - Alice already has Bob in her buddy list.
NAT and firewalls
D-tubes
D-tubes are tubes which have RPC running over them (in particular, D-Bus). This means that the libraries take care of the marshalling and so on - admittedly an easy part of RPC, but there's no call to reinvent the wheel... The alternative are stream tubes which forward a TCP connection; these are useful to wrap existing protocols.
Steps
setup
You will need the following packages (on Debian or Ubuntu; equivalents on other systems):
python-telepathy telepathy-gabble ... (no doubt a bunch of others... TODO)
Make sure that you have telepathy set up. In GNOME, this is done by running the Empathy Instant Messenger, which uses Telepathy exclusively. In KDE, ??? TODO (but not urgent while QtDBus can't handle this in any case) .
One-to-one tubes currently only work over the server-based XMPP protocol; thus, you'll need to use GTalk or Jabber accounts. (Note: multi-user tubes also work over link-local XMPP; that is, telepathy-salut.) For testing, Xnest or some other way of opening up another session in a window is useful, so as to have two test users on the screen at once.
actually doing it
These steps are intended to be read together with the example, which shows how they fit together.
- Initialise the library
import dbus, telepathy as well as a bunch of constants from telepathy.constants and telepathy.interfaces
you'll need an event loop; chances are, you already have one in your GUI; if you don't, import dbus.glib which will magically set up an event loop for you
- Pick a service name and construct the corresponding path and tube types
The service name should begin with your domain name backwards, followed by a name; my domain name is baum.com.au and this is an example, so I use:
SERVICE = 'au.com.baum.example'
The path name is the same, but separated by slashes and with a leading slash:
PATH = '/' + SERVICE.replace('.', '/')or
PATH = '/au/com/baum/example'
In a couple of places, a single-word designator is required, so replace the dots with underscores:
TUBETYPE = SERVICE.replace('.', '_')or
TUBETYPE = 'au_com_baum_example'
Let Telepathy know that I'm handling this kind of tube; this is done by claiming a dbus name with a particular prefix
self.service_name = CLIENT+'.'+TUBETYPE self.name_ownership = dbus.service.BusName(self.service_name, bus=bus)
- Obtain list of buddies
- This is a multi-step process
get list of connections from the AccountManager
- this is done near the bottom of the example
- for each connection, request the member lists 'subscribe', 'publish', 'stored' and possibly also 'known'
(this is the get_roster() method in the example)
- The "known" channel is an obsolete name for "stored"; there's probably no harm in asking for it, but you aren't likely to get it.
- as the buddies come in, request their details, including CONNECTION_INTERFACE_CONTACT_CAPABILITIES
- as the details come in, check them and filter out buddies which can't handle this kind of tube
- depending on your application, may wish to show these buddies greyed out or something
- off-line buddies will be discarded here, as they have no capabilities; the example doesn't watch for them coming on-line
- This is a multi-step process
- Select buddy
- (that part's up to you...)
- The example picks all buddies which can handle the tube; under test conditions, that's going to be just one...
- Contact buddy
- Again, a multi-step process
- Request a channel
- Offer a tube
- When the tube changes state to OPEN, it can be used.
- The process for receiving tubes is similar
Have a HandleChannels method and make sure that your Interfaces property includes CLIENT_HANDLER and that you have a HandlerChannelFilter property
When HandleChannels is called by the system, Accept the tube.
- When the tube changes state to OPEN, it can be used.
- Again, a multi-step process
- Use connection
Use the two objects - me to send signals and other to call methods:
me.Hello('hello from %s'%getpass.getuser()) other.Method("xyzzy %s"%getpass.getuser(), reply_handler=self.reply_cb, error_handler=self.error_cb)
- Close connection
- ...
Further reading
Known issues
A few additional TODO items...
Blocking vs asynchronous
At the moment, the example code does a number of things with blocking that would be done asynchronously in a real program - you don't want your event loop to hang while the IM infrastructure retrieves some avatar that's expired from its cache. TODO: rewrite it to use the asynchronous calls (after I get it working).
Avatars etc
The contact information retrieved should probably include avatars and perhaps other details (aliases, status).

