summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYouness Alaoui <youness.alaoui@collabora.co.uk>2009-03-10 19:22:19 (GMT)
committerYouness Alaoui <youness.alaoui@collabora.co.uk>2009-03-10 19:22:19 (GMT)
commit6963faa02b73cac11944fe6a3172e3f96b2ec99f (patch)
treea19b3efa6726de314f62b1c496b393a7eb0a76d5
parentfeeced3f4ff0fa8a1353e83eb45028f448fb5cb6 (diff)
parent5f82b4167b156bf1a9aec4733fc3d936c3c0e1f9 (diff)
downloadtelepathy-doc-6963faa02b73cac11944fe6a3172e3f96b2ec99f.tar.gz
telepathy-doc-6963faa02b73cac11944fe6a3172e3f96b2ec99f.tar.xz
Merge branch 'master' of git://git.collabora.co.uk/git/telepathy-doc
Conflicts: docs/book/insert-examples.py
-rw-r--r--.gitignore1
-rw-r--r--docs/book/C/channel.xml221
-rwxr-xr-xdocs/book/insert-examples.py57
-rw-r--r--docs/examples/pygtk_chat_client/ChatWindow.py28
-rw-r--r--docs/examples/pygtk_chat_client/RosterWindow.py75
-rwxr-xr-xdocs/examples/pygtk_chat_client/example.py332
-rwxr-xr-xdocs/examples/python_iface_messaging/example.py7
7 files changed, 678 insertions, 43 deletions
diff --git a/.gitignore b/.gitignore
index 04770af..5d716b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ config.status
configure
depcomp
*.sw?
+*.pyc
diff --git a/docs/book/C/channel.xml b/docs/book/C/channel.xml
index 7ea4ebb..208d2aa 100644
--- a/docs/book/C/channel.xml
+++ b/docs/book/C/channel.xml
@@ -220,13 +220,6 @@
</tgroup>
</informaltable>
- <para>
- Telepathy-glib provides the
- <ulink url="&url_telepathy_glib_base;connection-requests.html#tp-cli-connection-interface-requests-call-create-channel"><function>tp_cli_connection_interface_requests_call_create_channel()</function></ulink>
- and <ulink url="&url_telepathy_glib_base;connection-requests.html#tp-cli-connection-interface-requests-call-ensure-channel"><function>tp_cli_connection_interface_requests_call_ensure_channel()</function></ulink>
- functions for this purpose.
- </para>
-
<warning>
<title>RequestChannel</title>
<para>
@@ -269,6 +262,41 @@
</para>
</warning>
+ <sect2 id="sec.channel.requesting.python">
+ <title>telepathy-python</title>
+
+ <para>
+ telepathy-python provides the
+ <classname>telepathy.client.Channel</classname> 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.
+ </para>
+
+ <para>
+ <classname>telepathy.client.Channel</classname> 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
+ <xref linkend="ex.channel.requesting.python.class"/>.
+ </para>
+
+ <example id="ex.channel.requesting.python.class"
+ file="python_iface_messaging/example.py">
+ <title>Inheriting telepathy.client.Channel</title>
+ </example>
+ </sect2>
+
+ <sect2 id="sect.channel.requesting.glib">
+ <title>telepathy-glib</title>
+
+ <para>
+ Telepathy-glib provides the
+ <ulink url="&url_telepathy_glib_base;connection-requests.html#tp-cli-connection-interface-requests-call-create-channel"><function>tp_cli_connection_interface_requests_call_create_channel()</function></ulink>
+ and <ulink url="&url_telepathy_glib_base;connection-requests.html#tp-cli-connection-interface-requests-call-ensure-channel"><function>tp_cli_connection_interface_requests_call_ensure_channel()</function></ulink>
+ functions for this purpose.
+ </para>
+
+ </sect2>
+
</sect1>
<sect1 id="sect-channel-contactlist">
@@ -633,7 +661,7 @@
<row>
<entry>Transient Chatroom</entry>
<entry>MSN conversation</entry>
- <entry>Handle_Type_None</entry>
+ <entry><type>Handle_Type_None</type></entry>
<entry><interfacename>Group</interfacename></entry>
<entry>Appears as an anonymous channel.</entry>
</row>
@@ -733,7 +761,7 @@
<ulink url="http://xmpp.org/extensions/xep-0071.html">XHTML-IM</ulink>.
</para>
- <sect3>
+ <sect3 id="sect.channel.text.messages.format">
<title>Message Structure</title>
<para>
@@ -834,6 +862,181 @@
</tgroup>
</informaltable>
</example>
+
+ <para>
+ The known headers for a message are:
+ </para>
+
+ <informaltable>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Key</entry>
+ <entry>Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>message-token</entry>
+ <entry>String</entry>
+ <entry>
+ An opaque, globally-unique identifier for the entire message. Which
+ may be treated the same as a MIME message-id for the mid: and cid:
+ URI schemes. Not always present.
+ </entry>
+ </row>
+
+ <row>
+ <entry>message-sent, message-received</entry>
+ <entry><type>Unix_Timestamp64</type></entry>
+ <entry>
+ The time the message was sent and received respectively. May not
+ be present if a time cannot be determined.
+ </entry>
+ </row>
+
+ <row>
+ <entry>message-sender</entry>
+ <entry><type>Contact_Handle</type></entry>
+ <entry>
+ The handle id of the contact who sent the message. May be 0 or
+ ommitted, if the sender cannot be determined.
+ </entry>
+ </row>
+
+ <row>
+ <entry>message-type</entry>
+ <entry><type>Channel_Text_Message_Type</type></entry>
+ <entry>
+ The type of message. Defaults to
+ <type>Channel_Text_Message_Type_Normal</type> if ommitted.
+ </entry>
+ </row>
+
+ <row>
+ <entry>pending-message-id</entry>
+ <entry><type>Message_ID</type></entry>
+ <entry>
+ The incoming message ID, only valid for as long as the message
+ is unacknowledged.
+
+ <important><para>
+ This header is important for acknowledging the received message.
+ </para></important>
+ </entry>
+ </row>
+
+ <row>
+ <entry>interface</entry>
+ <entry><type>DBus_Interface</type></entry>
+ <entry>
+ This message is specific to the given interface, which is neither
+ Text nor Messages. This key can also appear in subsequent
+ parts of the message.
+ </entry>
+ </row>
+
+ <row>
+ <entry>scrollback</entry>
+ <entry>Boolean</entry>
+ <entry>
+ If true, the incoming message was part of a replay of message
+ history. Defaults to false.
+ </entry>
+ </row>
+
+ <row>
+ <entry>rescued</entry>
+ <entry>Boolean</entry>
+ <entry>
+ If true, the incoming message has been seen in a previous channel
+ during the lifetime of the Connection, but had not been
+ acknowledged when that channel closed. Defaults to false.
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>
+ Alternatives with the same name should be ordered from highest
+ fidelity to lowest fidelity (i.e. rich text before plain text).
+ Clients should display the first alternative they understand.
+ </para>
+
+ </sect3>
+
+ <sect3 id="sect.channel.text.messages.receiving">
+ <title>Receiving Messages</title>
+
+ <para>
+ When a remote user initiates a new text chat, Telepathy will
+ automatically create a new channel for the chat, who's creation will
+ be heralded by the <methodname>NewChannels</methodname> signal.
+ Filter on the channels given for channels of type
+ <type>Channel_Type_Text</type>.
+ <xref linkend="ex.channel.text.messages.newchannels"/> shows
+ how to discover this channel.
+ </para>
+
+ <example id="ex.channel.text.messages.receiving.newchannels"
+ file="python_iface_messaging/example.py">
+ <title>Discovering Incoming Channels</title>
+ </example>
+
+ <para>
+ Set up a D-Bus proxy for the channel, like you would for any other
+ channel (see <xref linkend="sec-channel-requesting"/>). Check to
+ ensure that the <interfacename>Messages</interfacename> interface
+ is available.
+ </para>
+
+ <para>
+ Incoming messages are announced with the
+ <methodname>MessageReceived</methodname> signal, which places them on
+ the pending message queue. To remove messages from the pending
+ message queue, they must be acknowledged with
+ <methodname>Text.AcknowledgePendingMessages</methodname>. There will
+ already be messages pending, the ones that caused the creation of
+ the channel. These can be accessed via the
+ <property>PendingMessages</property> property.
+ See <xref linkend="ex.channel.text.messages.receiving.main"/>.
+ </para>
+
+ <para>
+ The message id to acknowledge the message is contained in the headers
+ of the message as the key <literal>pending-message-id</literal>
+ (see <xref linkend="sect.channel.text.messages.format"/>).
+ </para>
+
+ <important>
+ <title>Always Acknowledge Messages</title>
+ <para>
+ You must always acknowledge a received message, but you should only
+ do so after you have attempted to parse it.
+ </para>
+ <para>
+ Failure to acknowledge a message will result in a new channel
+ being created with the pending messages when the current channel is
+ closed.
+ </para>
+ <para>
+ It's posssible that a bug in your application's message parser could
+ cause an application crash.
+ You should attempt to parse the message before acknowledging it.
+ Thus, if your application has a crash, the message will still be
+ pending when the client reloads.
+ </para>
+ </important>
+
+ <example id="ex.channel.text.messages.receiving.main"
+ file="python_iface_messaging/example.py">
+ <title>Setting Up and Receiving Messages</title>
+ </example>
+
</sect3>
</sect2>
diff --git a/docs/book/insert-examples.py b/docs/book/insert-examples.py
index 24c8090..f6fccc4 100755
--- a/docs/book/insert-examples.py
+++ b/docs/book/insert-examples.py
@@ -15,6 +15,7 @@
import sys
import os.path
+import re
from lxml import etree
doc = etree.parse (sys.stdin)
@@ -49,28 +50,20 @@ for example in examples:
if id:
print >> sys.stderr, "Including `%s' from `%s'..." % (id, filename)
-
+
+ begin_re = re.compile ('begin %s(\\s|$)' % id)
+ end_re = re.compile ('end %s(\\s|$)' % id)
+
begin = False
lines = []
for line in contents.split ('\n'):
- if begin:
- idx = line.find ('end %s' % id)
- l = len('end %s' % id)
- if idx != -1 and \
- (len(line) == idx + l or \
- line[idx + l] == ' '):
- break
- else:
- lines.append (line)
- else:
- idx = line.find ('begin %s' % id)
- l = len('begin %s' % id)
-
- if idx != -1 and \
- (len(line) == idx + l or \
- line[idx + l] == ' '):
- begin = True
- continue
+ if begin and end_re.search (line):
+ break
+ elif begin:
+ lines.append (line)
+ elif not begin and begin_re.search (line):
+ begin = True
+ continue
if lines != []: contents = '\n'.join (lines)
@@ -102,19 +95,11 @@ for nicename in included_files:
# find the starting offsets for each id in the file
def get_tuple (prefix, id):
str = '%s %s' % (prefix, id)
- start = 0
- while start < len(contents):
- idx = contents.find (str, start)
- start = idx + 1
- l = len(str)
- if idx != -1 and \
- (len(contents) == idx + l or \
- contents[idx + l] == ' ' or \
- contents[idx + l] == '\n'):
- return (idx, prefix, id, len (str))
- elif idx == -1:
- return (idx, prefix, id, len (str))
-
+ m = re.search (str + '(\\s|$)', contents)
+ if m is None: idx = None
+ else:
+ idx = m.span()[0]
+ return (idx, prefix, id, len (str))
offsets = map (lambda id: get_tuple ('begin', id),
included_files[nicename]) + \
map (lambda id: get_tuple ('end', id),
@@ -126,6 +111,8 @@ for nicename in included_files:
elem = None
for (offset, prefix, id, l) in offsets:
+ if offset is None: continue
+
# append the CDATA to elem
cdata = contents[cumoff:offset]
if elem is not None: elem.tail = cdata
@@ -140,6 +127,10 @@ for nicename in included_files:
pl.append (em)
elem = em
- elem.tail = contents[cumoff:]
+
+ if elem is None:
+ pl.text = contents[cumoff:]
+ else:
+ elem.tail = contents[cumoff:]
sys.stdout.write (etree.tostring (doc))
diff --git a/docs/examples/pygtk_chat_client/ChatWindow.py b/docs/examples/pygtk_chat_client/ChatWindow.py
new file mode 100644
index 0000000..51c8dff
--- /dev/null
+++ b/docs/examples/pygtk_chat_client/ChatWindow.py
@@ -0,0 +1,28 @@
+import gtk
+import gobject
+
+class ChatWindow(gtk.Window):
+ def __init__(self, channel):
+ super(ChatWindow, self).__init__()
+
+ self.sm = channel.sm
+ self.channel = channel
+
+ self.set_default_size(300, 300)
+ self.set_title('Chat with %s' % channel.target_id)
+
+ vbox = gtk.VBox()
+ self.add(vbox)
+
+ sw = gtk.ScrolledWindow()
+ vbox.pack_start (sw)
+
+ self.buffer = gtk.TextBuffer()
+ tv = gtk.TextView(self.buffer)
+ sw.add(tv)
+
+ vbox.show_all()
+
+ self.connect('destroy', lambda s: s.channel.close())
+
+gobject.type_register(ChatWindow)
diff --git a/docs/examples/pygtk_chat_client/RosterWindow.py b/docs/examples/pygtk_chat_client/RosterWindow.py
new file mode 100644
index 0000000..55b1b31
--- /dev/null
+++ b/docs/examples/pygtk_chat_client/RosterWindow.py
@@ -0,0 +1,75 @@
+import gtk
+import gobject
+
+from ChatWindow import ChatWindow
+
+class RosterWindow(gtk.Window):
+
+ def __init__(self, sm):
+ self.sm = sm
+ super(RosterWindow, self).__init__()
+
+ self.set_default_size(200, 400)
+
+ vbox = gtk.VBox()
+ self.add(vbox)
+
+ sw = gtk.ScrolledWindow()
+ vbox.pack_start (sw)
+
+ self.contacts = gtk.ListStore(gobject.TYPE_PYOBJECT)
+ tv = gtk.TreeView(self.contacts)
+ sw.add(tv)
+
+ vbox.show_all()
+
+ self.sm.connect('contacts-updated', self._contacts_updated)
+ self.sm.connect('new-chat', self._new_chat)
+
+ # set up the treeview
+ renderer = gtk.CellRendererText()
+ column = gtk.TreeViewColumn('Contacts', renderer)
+ tv.append_column(column)
+
+ def text_func(column, cell, model, iter):
+ contact = model.get_value(iter, 0)
+ cell.set_property('markup', '%s\n<span size="small" style="italic">%s</span>' % (
+ contact.alias, contact.get_status()))
+
+ column.set_cell_data_func(renderer, text_func)
+
+ def sort_func(model, iter1, iter2):
+ a = model.get_value(iter1, 0)
+ b = model.get_value(iter2, 0)
+ return cmp(a,b)
+
+ self.contacts.set_default_sort_func(sort_func)
+ self.contacts.set_sort_column_id(-1, gtk.SORT_ASCENDING)
+
+ def _contacts_updated(self, sm, updated):
+ # build a set of contacts in the state machine
+ contacts = set(self.sm.contacts.values())
+ updated = set(updated)
+
+ # iterate through the liststore, updating the contacts
+ for row in self.contacts:
+ contact = row[0]
+
+ if contact in updated or \
+ contact not in contacts:
+ self.contacts.remove(row.iter)
+ continue
+
+ contacts.remove(contact)
+
+ # iterate through the remaining items in the set, adding them to the
+ # list store
+ for contact in contacts:
+ self.contacts.append ((contact,))
+
+ def _new_chat(self, sm, channel):
+ w = ChatWindow(channel)
+ w.set_transient_for(self)
+ w.show()
+
+gobject.type_register(RosterWindow)
diff --git a/docs/examples/pygtk_chat_client/example.py b/docs/examples/pygtk_chat_client/example.py
new file mode 100755
index 0000000..8e5c264
--- /dev/null
+++ b/docs/examples/pygtk_chat_client/example.py
@@ -0,0 +1,332 @@
+#!/usr/bin/env python
+
+import sys
+
+import gtk
+import gobject
+import dbus.mainloop.glib
+dbus.mainloop.glib.DBusGMainLoop(set_as_default = True)
+
+import telepathy
+import telepathy.client
+from telepathy.interfaces import CONNECTION_MANAGER, \
+ CONNECTION, \
+ CONNECTION_INTERFACE_REQUESTS, \
+ CONNECTION_INTERFACE_CONTACTS, \
+ CONNECTION_INTERFACE_ALIASING, \
+ CONNECTION_INTERFACE_SIMPLE_PRESENCE, \
+ CHANNEL, \
+ CHANNEL_TYPE_CONTACT_LIST, \
+ CHANNEL_TYPE_TEXT, \
+ CHANNEL_INTERFACE_MESSAGES, \
+ CHANNEL_INTERFACE_GROUP
+from telepathy.constants import CONNECTION_STATUS_CONNECTED, \
+ CONNECTION_STATUS_DISCONNECTED, \
+ CONNECTION_PRESENCE_TYPE_AVAILABLE, \
+ CONNECTION_PRESENCE_TYPE_BUSY, \
+ CONNECTION_PRESENCE_TYPE_AWAY, \
+ CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY, \
+ CONNECTION_PRESENCE_TYPE_UNSET, \
+ CONNECTION_PRESENCE_TYPE_UNKNOWN, \
+ CONNECTION_PRESENCE_TYPE_OFFLINE, \
+ HANDLE_TYPE_CONTACT, \
+ HANDLE_TYPE_LIST, \
+ HANDLE_TYPE_GROUP
+
+DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
+
+def generic_reply(*args): pass
+
+class Connection(telepathy.client.Connection):
+ def __init__(self, sm, bus_name, object_path):
+ super(Connection, self).__init__(bus_name, object_path)
+
+ self.sm = sm
+
+ self[CONNECTION].connect_to_signal('StatusChanged', self.status_changed)
+ self[CONNECTION].Connect(reply_handler = generic_reply,
+ error_handler = self.sm.error)
+
+ def status_changed(self, status, reason):
+ if status == CONNECTION_STATUS_DISCONNECTED:
+ print 'Disconnected!'
+ # FIXME: signal disconnection with the SM
+ gtk.main_quit()
+ return
+
+ elif status != CONNECTION_STATUS_CONNECTED:
+ return
+
+ print 'Carrier Detected'
+ # NB. this should be a property eventually
+ self[CONNECTION].GetInterfaces(reply_handler = self._interfaces_cb,
+ error_handler = self.sm.error)
+
+ def _interfaces_cb(self, interfaces):
+ self.interfaces = interfaces
+
+ self.sm.connection_ready(self)
+
+ def disconnect(self):
+ self[CONNECTION].Disconnect(reply_handler = generic_reply,
+ error_handler = self.sm.error)
+
+ def ensure_channel(self, channel_obj, handle_type, target_id, props = {}):
+
+ d = {
+ CHANNEL + '.ChannelType' : channel_obj.channel_type,
+ CHANNEL + '.TargetHandleType' : handle_type,
+ CHANNEL + '.TargetID' : target_id,
+ }
+
+ d.update(props)
+
+ def _ensure_channel_error(self, error):
+ print 'Channel could not be created for dict: %s' % d
+
+ if CONNECTION_INTERFACE_REQUESTS in self.interfaces:
+ self[CONNECTION_INTERFACE_REQUESTS].EnsureChannel(d,
+ reply_handler = lambda a, b, c: channel_obj(self, b, c, a),
+ error_handler = self._ensure_channel_error)
+ else:
+ self.sm.error("Requests interface unavailable, get a better CM")
+
+class Channel(telepathy.client.Channel):
+ def __init__(self, conn, object_path, props, yours = False):
+ self.conn = conn
+ self.sm = conn.sm
+ self.yours = yours
+
+ self.target_id = props[CHANNEL + '.TargetID']
+ self.handle = props[CHANNEL + '.TargetHandle']
+ self.handle_type = props[CHANNEL + '.TargetHandleType']
+
+ print 'Channel came up... requesting interfaces'
+ super(Channel, self).__init__(conn.service_name, object_path)
+
+ self[DBUS_PROPERTIES].Get (CHANNEL, 'Interfaces',
+ reply_handler = self._interfaces_cb,
+ error_handler = self.sm.error)
+
+ def close(self):
+ self[CHANNEL].Close(reply_handler = generic_reply,
+ error_handler = self.sm.error)
+
+ def get_target_id(self):
+ return self.target_id
+
+ def _interfaces_cb(self, interfaces):
+ self.interfaces = interfaces
+
+ self.ready()
+
+ def ready(self):
+ pass
+
+class ContactList(Channel):
+ channel_type = CHANNEL_TYPE_CONTACT_LIST
+
+ def ready(self):
+ # get the list of handles
+ if CHANNEL_INTERFACE_GROUP in self.interfaces:
+ self[DBUS_PROPERTIES].Get(CHANNEL_INTERFACE_GROUP, 'Members',
+ reply_handler = self._members_cb,
+ error_handler = self.sm.error)
+ else:
+ print 'Channel does not implement Group... strange'
+
+ def _members_cb(self, handles):
+ # these are the interfaces we're seeking
+ interfaces = [
+ CONNECTION_INTERFACE_ALIASING,
+ CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+ ]
+
+ # work out what interfaces are available
+ interfaces = list (set(interfaces) & set(self.conn.interfaces))
+ interfaces += [ CONNECTION ]
+
+ # look them up via the contacts interface
+ if CONNECTION_INTERFACE_CONTACTS in self.conn.interfaces:
+ self.conn[CONNECTION_INTERFACE_CONTACTS].GetContactAttributes(
+ handles, interfaces, False,
+ reply_handler = self._attributes_cb,
+ error_handler = self.sm.error)
+
+ def _attributes_cb(self, map):
+ for handle, attributes in map.iteritems():
+ contact = Contact (self.sm, handle, attributes)
+ self.sm.contacts[handle] = contact
+
+ self.sm.contacts_updated(map.keys())
+
+class TextChannel(Channel):
+ channel_type = CHANNEL_TYPE_TEXT
+
+ def ready(self):
+ self.sm.new_text_channel(self)
+
+class Contact(object):
+ def __init__(self, sm, handle, attributes={}):
+ self.sm = sm
+ self.handle = handle
+
+ for key, value in attributes.iteritems():
+ if key == CONNECTION + '/contact-id':
+ self.contact_id = value
+ elif key == CONNECTION_INTERFACE_ALIASING + '/alias':
+ self.alias = value
+ elif key == CONNECTION_INTERFACE_SIMPLE_PRESENCE + '/presence':
+ self.presence = value
+
+ def get_state(self):
+ return self.presence[0]
+
+ def get_status(self):
+ if self.presence[2] != '':
+ return self.presence[2]
+ else:
+ return self.presence[1]
+
+ def __repr__(self):
+ return 'Contact(%s)' % self.contact_id
+
+ def __cmp__(self, other):
+ ordering_map = {
+ CONNECTION_PRESENCE_TYPE_AVAILABLE : 0,
+ CONNECTION_PRESENCE_TYPE_BUSY : 1,
+ CONNECTION_PRESENCE_TYPE_AWAY : 2,
+ CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY : 3,
+ CONNECTION_PRESENCE_TYPE_UNSET : 4,
+ CONNECTION_PRESENCE_TYPE_UNKNOWN : 4,
+ CONNECTION_PRESENCE_TYPE_OFFLINE : 5,
+ }
+
+ c = cmp (ordering_map[self.get_state()], ordering_map[other.get_state()])
+ if c != 0: return c
+ c = cmp (self.alias, other.alias)
+ return c
+
+class StateMachine(gobject.GObject):
+ __gsignals__ = {
+ 'contacts-updated' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_PYOBJECT,)),
+ 'new-chat' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_PYOBJECT,)),
+ }
+
+ def __init__(self):
+ super(StateMachine, self).__init__()
+
+ self.contacts = {}
+
+ def tp_connect(self, account, password):
+ """e.g. account = 'bob@example.com/test'
+ password = 'bigbob'
+ """
+
+ reg = telepathy.client.ManagerRegistry()
+ reg.LoadManagers()
+
+ # get the gabble Connection Manager
+ self.cm = cm = reg.GetManager('gabble')
+
+ print 'Connecting...'
+ cm[CONNECTION_MANAGER].RequestConnection('jabber',
+ {
+ 'account': account,
+ 'password': password,
+ },
+ reply_handler = lambda a, b: Connection (self, a, b),
+ error_handler = self.error)
+
+ def error(self, error):
+ print "Telepathy Error: %s" % error
+ print "Disconnecting..."
+ self.tp_disconnect()
+
+ def connection_ready(self, conn):
+ print "Connection Ready"
+ self.conn = conn
+
+ # set up signals for interfaces we're interested in
+ if CONNECTION_INTERFACE_ALIASING in conn.interfaces:
+ conn[CONNECTION_INTERFACE_ALIASING].connect_to_signal(
+ 'AliasesChanged', self._aliases_changed)
+
+ if CONNECTION_INTERFACE_SIMPLE_PRESENCE in conn.interfaces:
+ conn[CONNECTION_INTERFACE_SIMPLE_PRESENCE].connect_to_signal(
+ 'PresencesChanged', self._presences_changed)
+
+ if CONNECTION_INTERFACE_REQUESTS in conn.interfaces:
+ conn[CONNECTION_INTERFACE_REQUESTS].connect_to_signal(
+ 'NewChannels', self._new_channels)
+ else:
+ self.sm.error("Requests interface unavailable, get a better CM")
+
+ # request the contact lists
+ print 'Requesting roster...'
+ self.conn.ensure_channel (ContactList, HANDLE_TYPE_LIST, 'subscribe')
+ self.conn.ensure_channel (ContactList, HANDLE_TYPE_LIST, 'publish')
+
+ def new_text_channel(self, channel):
+ self.emit('new-chat', channel)
+
+ def _aliases_changed(self, aliases):
+ for handle, alias in aliases:
+ if handle not in self.contacts: continue
+ self.contacts[handle].alias = alias
+
+ self.contacts_updated(map(lambda (h, a): h, aliases))
+
+ def _presences_changed(self, presences):
+ for handle, presence in presences.iteritems():
+ if handle not in self.contacts: continue
+ self.contacts[handle].presence = presence
+
+ self.contacts_updated(presences.keys())
+
+ def _new_channels(self, channels):
+ for channel_path, props in channels:
+ channel_type = props[CHANNEL + '.ChannelType']
+
+ if channel_type == TextChannel.channel_type:
+ TextChannel(self.conn, channel_path, props)
+
+ def contacts_updated(self, handles):
+ contacts = [ self.contacts[h] for h in handles if h in self.contacts]
+
+ self.emit('contacts-updated', contacts)
+
+ def tp_disconnect(self):
+ print 'Disconnecting...'
+ try:
+ self.conn.disconnect()
+ except:
+ gtk.main_quit()
+
+gobject.type_register(StateMachine)
+
+if __name__ == '__main__':
+ import getpass
+
+ from RosterWindow import RosterWindow
+
+ account = sys.argv[1]
+ password = getpass.getpass()
+
+ sm = StateMachine()
+ roster = RosterWindow(sm)
+ roster.show()
+
+ sm.tp_connect(account, password)
+ roster.connect ('destroy', lambda w: sm.tp_disconnect())
+
+ try:
+ print 'Running...'
+ gtk.main()
+ except KeyboardInterrupt:
+ print "Terminating connection..."
+ sm.tp_disconnect()
+ # reengage the mainloop so that we can disconnect cleanly
+ gtk.main()
diff --git a/docs/examples/python_iface_messaging/example.py b/docs/examples/python_iface_messaging/example.py
index 2fefd88..5563964 100755
--- a/docs/examples/python_iface_messaging/example.py
+++ b/docs/examples/python_iface_messaging/example.py
@@ -21,18 +21,20 @@ from telepathy.constants import CONNECTION_STATUS_CONNECTED, \
DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
+# begin ex.channel.requesting.python.class
class TextChannel (telepathy.client.Channel):
def __init__ (self, parent, channel_path):
self.parent = parent
conn = parent.conn
super (TextChannel, self).__init__ (conn.service_name, channel_path)
+ # end ex.channel.requesting.python.class
channel = self
channel[DBUS_PROPERTIES].Get(CHANNEL, 'Interfaces',
reply_handler = self.interfaces_cb,
error_handler = self.parent.error_cb)
-
+ # begin ex.channel.text.messages.receiving.main
def interfaces_cb (self, interfaces):
channel = self
@@ -64,6 +66,7 @@ class TextChannel (telepathy.client.Channel):
channel[CHANNEL_TYPE_TEXT].AcknowledgePendingMessages([msg_id],
reply_handler = self.parent.generic_reply,
error_handler = self.parent.error_cb)
+ # end ex.channel.text.messages.receiving.main
print '-' * 78
print 'Received Message:'
@@ -159,6 +162,7 @@ class Example (object):
conn[CONNECTION].GetInterfaces(reply_handler = self.get_interfaces_cb,
error_handler = self.error_cb)
+ # begin ex.channel.text.messages.receiving.newchannels
def get_interfaces_cb (self, interfaces):
conn = self.conn
@@ -176,6 +180,7 @@ class Example (object):
print 'New chat from %s' % props[CHANNEL + '.TargetID']
# let's hook up to this channel
TextChannel(self, channel)
+ # end ex.channel.text.messages.receiving.newchannels
if __name__ == '__main__':
import getpass