FOAF and Gaim

Like many people who spend who use AIM, I’ve spent a lot of time perfecting my buddy list. It seemed a shame that this effort should go to waste, so I started trying to think of what else I could use this data for. It occured to me that this list represents a good chunk of the people with whom I communicate every day, i.e., friends and acquaintances, which sounds a lot like what FOAF is all about. I use Gaim as my IM client of choice, and so I started poking around in my ~/.gaim directory for interesting files. I found one: blist.xml, which appears to be an XML version of my buddy list. With a little work, some XSL, and a few assumptions, I found that I could generate FOAF fragments from this buddy list fairly easily.

The format of blist.xml is pretty simple:

<?xml version='1.0' encoding='UTF-8' ?>
<gaim version="1">
<blist>
<group name="People">
<setting name="collapsed" type="bool">0</setting>
<contact>
<buddy account="fcd1ce2cae0cd125" proto="prpl-oscar" protocol="1">
<name>eb0da4b4fbbcbf13</name>
<alias>dlcbot</alias>
<setting name="buddy_icon" type="string">/home/dlc/.gaim/icons/f2918e99</setting>
<setting name="icon_checksum" type="string">6e6abfcdeec16cab410fa23fd5d18536</setting>
</buddy>
</contact>
</group>
</blist>
<privacy>
<account proto="prpl-oscar" name="fcd1ce2cae0cd125" mode="1" protocol="1">
</account>
</privacy>
</gaim>

The important elements of this file are the <buddy> elements within the <blist> element. Each of these represents a “buddy”. Each of these will correspond to a <foaf:Person> element in the emitted FOAF. I fudge a bit and assume that the first <account> element in the <privacy> element is my primary account (it is, in my case, but I don’t know enough about gaim to assume that this is a how gaim always operates). This element becomes the RDF node that the FOAF document is about:

<foaf:Person rdf:ID="fcd1ce2cae0cd125">
<foaf:knows rdf:nodeID="eb0da4b4fbbcbf13" />
</foaf:Person>

Associating the foaf:Person (which represents me, here) with the node representing me in another file is something that I haven’t quite worked out the details of. I have some ideas though; by specifying rdf:ID for this foaf:Person element, I can reference it externally, which means that I should be able to reference this file from my main FOAF document using something like this:

<foaf:Person rdf:nodeID="me">
<rdfs:seeAlso rdf:resource="blist.rdf#fcd1ce2cae0cd125" />
</foaf:Person>

Each <buddy> element can be turned into a simple foaf:Person node with the elements foaf:aimChatID (obviously), foaf:nick, and, possibly, foaf:Image (if they have an icon defined). This is accomplished with this xsl:template:

<xsl:template match="buddy">
<foaf:Person>
<xsl:attribute name="rdf:nodeID">
<xsl:value-of select="name/text()"/>
</xsl:attribute>
<foaf:aimChatID><xsl:value-of select="name/text()" /></foaf:aimChatID>
<xsl:choose>
<xsl:when test="alias/text()">
<foaf:nick><xsl:value-of select="alias/text()" /></foaf:nick>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="setting[@name='buddy_icon']/text()">
<foaf:Image>
<xsl:attribute name="rdf:resource">
<xsl:text>file://</xsl:text>
<xsl:value-of select="setting[@name='buddy_icon']/text()" />
</xsl:attribute>
<dc:description>
<xsl:text>Icon of </xsl:text>
<xsl:value-of select="name/text()"/>
<xsl:text>.</xsl:text>
</dc:description>
</foaf:Image>
</xsl:when>
</xsl:choose>
</foaf:Person>
</xsl:template>

For example, the entry for my buddy eb0da4b4fbbcbf13 (who is a real AIM user) would look like:

<foaf:Person rdf:nodeID="eb0da4b4fbbcbf13">
<foaf:aimChatID>eb0da4b4fbbcbf13</foaf:aimChatID>
<foaf:nick>dlcbot</foaf:nick>
<foaf:Image rdf:resource="file:///home/dlc/.gaim/icons/f2918e99">
<dc:description>Icon of eb0da4b4fbbcbf13.</dc:description>
</foaf:Image>
</foaf:Person>

OK, maybe it’s not the best foaf:Person entry you’ve ever seen, but it’s not too bad when you consider I got it practically for free.

The only other piece is the template to generate the main foaf:Person bit, which is fairly simple. Keep in mind that we’re working with a very small amount of data here.

<xsl:template match="account">
<foaf:Person>
<xsl:attribute name="rdf:ID">
<xsl:value-of select="@name"/>
</xsl:attribute>
<xsl:for-each select="/gaim/blist//buddy[@account = /gaim/privacy/account[1]/@name]">
<foaf:knows>
<xsl:attribute name="rdf:nodeID">
<xsl:value-of select="name/text()"/>
</xsl:attribute>
</foaf:knows>
</xsl:for-each>
</foaf:Person>
</xsl:template>

Remember I’m generating a rdf:ID attribute, instead of rdf:nodeID, so that this node can be referenced from external documents. And that’s all of it, basically; the template for / is only two elements and a preamble:

<xsl:template match="/">
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:foaf="http://xmlns.com/foaf/0.1/">
<!-- Assumes that the first item in /gaim/privacy/account
is the "real" account -->
<xsl:apply-templates select="/gaim/privacy/account[1]"/>
<xsl:apply-templates select="/gaim/blist//buddy[@account = /gaim/privacy/account[1]/@name]" />
</rdf:RDF>
</xsl:template>

Note that the second xsl:apply-templates is limited to buddy elements that match the name attribute of first account element. This is primarily because, even though gaim allows multiple accounts (i.e., multiple account elements), modelling this in FOAF was too difficult for me.

The entire stylesheet can be downloaded. I’d be happy to hear if anyone has any comments or comes up with anything interesting.

Advertisements
%d bloggers like this: