Channels may be requested from the Channel Dispatcher or directly from a Connection object using the Requests interface.
Requesting a channel from the dispatcher will cause it to be handled like any other dispatched channel. This is the preferred way of requesting new channels.
Requesting a channel directly from the Connection will cause it to be ignored by the Channel Dispatcher, meaning that the requesting application must handle the channel itself.
Channels requested from the Channel Dispatcher will be dispatched via the same mechanism as an unrequested, incoming channel. This means that the best available application to handle the channel will be used.
Requesting channels through the Channel Dispatcher also allows the Dispatcher to try a different client if the first client fails to handle the channel.
If you wish to handle the request yourself, you can pass the D-Bus well-known name of your client as the preferred handler.
These interfaces provides two methods: CreateChannel and EnsureChannel. Both methods take the same parameter, a dictionary containing the desired properties for the channel.
CreateChannel will attempt to create a new channel with the requested properties. Depending on the protocol, some types of channels are exclusive, and only one such channel can exist at a time (e.g. a XMPP chatroom). If a second channel is requested, the error NotAvailable is returned.
Conversely, EnsureChannel will attempt to reuse an existing channel with the same properties wherever possible, else it will create a new channel. It's possible that another client is also utilising this channel.
Choosing when to always create a new channel, or when to use an existing channel can usually be deduced based on the function of the channel. If it would make sense to reuse an existing channel then use EnsureChannel, otherwise use CreateChannel.
EnsureChannel is usually used for Text, StreamedMedia and ContactList channels.
CreateChannel is usually used for FileTransfer, Tubes, RoomList and ContactSearch channels.
The properties argument for CreateChannel and EnsureChannel is a map of property names on the desired channel, and their values. In general every channel requires at least three channel properties: the type of channel we wish to create (ChannelType), the handle/id of the contact/room/list we wish to create a channel for (TargetHandle or TargetID) and the type of that handle (TargetHandleType). Specific channel types may require additional properties in order to be created, this is noted in the specification.
For example, to create a ContactList channel (this is the type of channel that is used to get a list of subscribed contacts from a service), we might provide a map like so:
org.freedesktop.Telepathy.Channel.ChannelType | org.freedesktop.Telepathy.Channel.Type.ContactList |
org.freedesktop.Telepathy.Channel.TargetHandleType | Handle_Type_List |
org.freedesktop.Telepathy.Channel.TargetID | "subscribe" |
Channel requests should only include one of the properties TargetHandle or TargetID. Providing both properties (even if they match) is an error and your request will fail.
More channel request examples are given in Example 6-1.
Anonymous channels are channels that do not connect to a remote (single) contact, room, list or group. For example, RoomList channels, or anonymous MUC chats (e.g. in MSN).
They are requested by giving a TargetHandleType of Handle_Type_None (you should not specify a TargetHandle for an anonymous channel).
When using the Requests interface to request a channel directly from a Connection, both EnsureChannel and CreateChannel return the object path of a channel that can be access on that Connection.
Using the Channel Dispatcher, requests might take some time (e.g. if the account has to be brought online), so you are instead returned a Channel Request Object, which is discussed further in Section 6.1.1 ― Channel Request Objects.
Requesting a one-to-one text channel to the contact escher@tuxedo.cat:
Channel.ChannelType | Channel.Type.Text |
Channel.TargetHandleType | Handle_Type_Contact |
Channel.TargetID | escher@tuxedo.cat |
Requesting a one-to-one text channel to the contact with handle id 12 (as requested using RequestHandles), or from a TpContact, or a method-return/signal that gives handles:
Channel.ChannelType | Channel.Type.Text |
Channel.TargetHandleType | Handle_Type_Contact |
Channel.TargetHandle | 12 |
Requesting a text channel to the multi-user chat room kitties@conf.litter.cat:
Channel.ChannelType | Channel.Type.Text |
Channel.TargetHandleType | Handle_Type_Room |
Channel.TargetID | kitties@conf.litter.cat |
Requesting a D-Bus tube to cami@egg.cat for the service cat.litter.ExampleService (see more of these in Section 11.1 ― Creating a Tube):
Channel.ChannelType | Channel.Type.DBusTube |
Channel.TargetHandleType | Handle_Type_Contact |
Channel.TargetID | cami@egg.cat |
Channel.Type.DBusTube.ServiceName | cat.litter.ExampleService |
Opening a file transfer channel to harvey@nom.cat; initial information about the file is included in the channel request so that the recepient can make a decision whether to accept the channel (more information in Section 9.1 ― Sending Files):
Channel.ChannelType | Channel.Type.FileTransfer |
Channel.TargetHandleType | Handle_Type_Contact |
Channel.TargetID | harvey@nom.cat |
Channel.Type.FileTransfer.Filename | cat.jpg |
Channel.Type.FileTransfer.ContentType | image/jpeg |
Channel.Type.FileTransfer.Size | 115121 |
Channels requested via the Channel Dispatcher don't immediately return the newly created channel from calls to CreateChannel and EnsureChannel. This is because the specified Account may not be immediately ready to request a channel, and the D-Bus method call may time out before the channel is requested.
In the worst case, the specified Account for a request may be offline and have to be brought online by the Account Manager. The network connection may only connect on demand (common on mobile devices) and may have to be brought up before the account can be connected.
To solve this problem, the Channel Dispatcher returns Channel Request Objects, which implement the ChannelRequest interface. These objects are accessible via the Channel Dispatcher well-known bus name (org.freedesktop.Telepathy.ChannelDispatcher).
Channel Requests provide two signals: Succeeded and Failed. You should connect these two signals and then call the Proceed method to begin the request. If you do not call Proceed, the request will not begin.
There is also a Cancel method that will allow you to cancel a method up until the time it has been dispatched to a Channel Handler. The precise semantics of this method are provided in the Telepathy specification.
telepathy-python provides the telepathy.client.Channel class as a D-Bus proxy object. To construct this proxy you need to pass the connection's D-Bus service name and the path to the channel object.
telepathy.client.Channel can be inherited like any other Python class, so that you can pass around all of the methods and state relating to that channel as one object. This is shown in Example 6-2.
Requesting channels from the Channel Dispatcher is the generally preferred way to create or ensure channels. telepathy-glib provides a class TpAccountChannelRequest that you can use to make channel requests that either you or another client can handle.
There are four main methods available via the TpAccountChannelRequest class:
The first two methods request a channel that can be dispatched to any channel handler (see Section 4.1 ― Telepathy Clients). You may optionally provide a name for the preferred Handler for this channel request. If you want to request a channel, but you don't care who handles it, use these methods. Example 6-3 is an example of ensuring a channel this way, the channel is most likely going to be handled by Empathy or some other chat client.
The second two methods request a channel that you wish to handle yourself. These methods work by creating a temporary Handler and giving its name as the preferred Handler for the channel request. If you want to request a channel to use yourself (and you want to get a TpChannel in your callback), use these methods.
props = tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_ROOM, TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, targetid, NULL); request = tp_account_channel_request_new (TP_ACCOUNT (account), props, TP_USER_ACTION_TIME_CURRENT_TIME); /* ensure this channel, but let the default handler handle it */ tp_account_channel_request_ensure_channel_async (request, NULL, NULL, _muc_channel_ready, NULL); g_hash_table_destroy (props); g_object_unref (request);
These methods follow the GIO async pattern, so you must call the appropriate "finish" method in the callback. Checking the return value and error let's you know the channel was successfully handled. TpChannels have the core feature prepared.
Operation | Launch Method/Finish Method | Return Value |
---|---|---|
Create | tp_account_channel_request_create_channel_async tp_account_channel_request_create_channel_finish | True if channel successfully handled |
Ensure | tp_account_channel_request_ensure_channel_async tp_account_channel_request_ensure_channel_finish | True if channel successfully handled |
Create and Handle | tp_account_channel_request_create_and_handle_channel_async tp_account_channel_request_create_and_handle_channel_finish | TpChannel if successful |
Ensure and Handle | tp_account_channel_request_ensure_and_handle_channel_async tp_account_channel_request_ensure_and_handle_channel_finish | TpChannel if successful |
If you are the channel handler, and the channel you've requested can be ensured by other clients (e.g. a text-channel), you should keep the TpAccountChannelRequest and listen to the re-handled signal. This signal will be emitted whenever someone else ensures the channel, and can be used to bring attention to the channel.
When handling a channel, the user action time can be used for things like focus stealing prevention.
User action times in Telepathy are subtley different to event times in X11. To help convert between them, telepathy-glib provides to convenience functions: tp_user_action_time_from_x11 and tp_user_action_time_should_present.
It is possible to request a channel directly from a Connection. telepathy-glib provides no high-level API to do this, because it's only intended to be used by the Channel Dispatcher, and not by Telepathy clients. Channels requested directly will be unknown to the Channel Dispatcher.
Example 6-4 shows ensuring a channel directly from a Connection.
/* explicitly ask for the publish and subscribe contact lists * these will be announced by NewChannels, so we don't need * to handle their callbacks (this does mean we also can't * handle their errors) */ GHashTable *request = tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_LIST, NULL); /* the 'publish' list */ tp_asv_set_string (request, TP_PROP_CHANNEL_TARGET_ID, "publish"); tp_cli_connection_interface_requests_call_ensure_channel ( conn, -1, request, NULL, NULL, NULL, NULL); /* the 'subscribe' list */ tp_asv_set_string (request, TP_PROP_CHANNEL_TARGET_ID, "subscribe"); tp_cli_connection_interface_requests_call_ensure_channel ( conn, -1, request, NULL, NULL, NULL, NULL); g_hash_table_destroy (request);
The TpChannel is used to work with channels in telepathy-glib. It also provides API to help use the Groups interface, that is present on many channels.
A TpChannel can either be created using tp_channel_new or tp_channel_new_from_properties. The latter is generally preferred as you can just pass in the returned property map, as shown in Example 6-5.
const char *type = tp_asv_get_string (map, TP_PROP_CHANNEL_CHANNEL_TYPE); /* if this channel is a contact list, we want to know * about it */ if (!strcmp (type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) { TpChannel *channel = tp_channel_new_from_properties ( conn, object_path, map, &error); handle_error (error); tp_channel_call_when_ready (channel, channel_ready, NULL); }
The RequestChannel method has been deprecated and should only be used if compatibility is required with older Connection Managers that do not implement the Requests interface.
Some Connection Managers might not yet implement the Requests interface on their Connections. In these cases you can use the RequestChannel method on the Connection interface, but you do so at your own risk.
CreateChannel and EnsureChannel fix several flaws that were present in RequestChannel:
If you must call RequestChannel, you call it with the same three arguments required for all channels (channel type, handle type and handle). The Suppress_Handler argument should always be True. Example 6-6 shows how to do this.
self.conn[CONNECTION].RequestChannel(CHANNEL_TYPE_ROOM_LIST, HANDLE_TYPE_NONE, 0, True, reply_handler = self.got_roomlist, error_handler = self.error) # begin example.channel.roomlist.listrooms def got_roomlist(self, channel_path): print 'Got Roomlist Channel' channel = telepathy.client.Channel(self.conn.service_name, channel_path)
When requesting an anonymous channel with RequestChannel, a channel with handle type Handle_Type_None (as is the case in Example 6-6), pass the handle id 0.