XMPP Jabber Photo Module

I was looking around for a XMPP vCard Photo Module in May of 2007, and could not find one, so I wrote my own. It uses the xmpppy python module for communicating with the jabber server. Once the connection is made with the server, call register_handler(session). It will then download avatars of people on the roster list. To get the filename of a person’s avatar, call get_photo(photo_hash).

import base64
import os
import sha
import xmpp

PHOTO_DIR = "./photos/"

PHOTO_TYPES = {
    'image/png': '.png',
    'image/jpeg': '.jpg',
    'image/gif': '.gif',
    'image/bmp': '.bmp',
    }

def append_directory(filename):
    return os.path.join(PHOTO_DIR, filename)

def register_handler(session):
    session.RegisterHandler('presence', photo_update_handler)

def photo_update_handler(session, stanza):
    JID = stanza['from'].getStripped()
    vupdate = stanza.getTag('x', namespace='vcard-temp:x:update')
    if not vupdate:
        return
    photo = vupdate.getTag('photo')
    if not photo:
        return
    photo = photo.getData()
    if not photo:
        return
    #request the photo only if we don't have it already
    if not get_photo(photo):
        request_vcard(session, JID)

def get_photo(photo_hash):
    for ext in PHOTO_TYPES.values():
        filepath = append_directory(photo_hash + ext)
        if os.path.exists(filepath):
            return filepath

def request_vcard(session, JID):
    n = xmpp.Node('vCard', attrs={'xmlns': xmpp.NS_VCARD})
    iq = xmpp.Protocol('iq', JID, 'get', payload=[n])
    return session.SendAndCallForResponse(iq, recieve_vcard)

def recieve_vcard(session, stanza):
    photo = stanza.getTag('vCard').getTag('PHOTO')
    if not photo:
        return
    photo_type = photo.getTag('TYPE').getData()
    photo_bin = photo.getTag('BINVAL').getData()
    photo_bin = base64.b64decode(photo_bin)
    ext = PHOTO_TYPES[photo_type]
    photo_hash = sha.new()
    photo_hash.update(photo_bin)
    photo_hash = photo_hash.hexdigest()
    filename = append_directory(photo_hash + ext)
    file(filename, 'wb').write(photo_bin)

UPDATE 7/20/09
Here is roughly how I would solve the use case in the comment. I realize that my code doesn’t solve that case very nicely. If it made this more object oriented I could solve that problem. Until then, here is some rough code that I think does what it needs to. Save the above code as photo.py and be sure to create a “photos” directory

import xmpp
import photo

jid_photo_map = {}

def receive_presence(session, stanza):
    jid = stanza['from'].getStripped()
    vupdate = stanza.getTag('x', namespace='vcard-temp:x:update')
    if not vupdate:
        return
    photo = vupdate.getTag('photo')
    if not photo:
        return
    photo = photo.getData()
    if not photo:
        return
    jid_photo_map[jid] = photo

def login(username, password):
    jabber = xmpp.Client('gmail.com')
    jabber.connect(server=('talk.google.com', 5223))
    jabber.auth(username, password, 'test_client')
    jabber.sendInitPresence()
    jabber.RegisterHandler('presence', receive_presence)
    photo.register_handler(jabber)
    return jabber

def display_filenames(j):
    roster = j.getRoster()
    for jid in roster.getItems():
        if jid in jid_photo_map:
            photo_filename = photo.get_photo(jid_photo_map[jid])
        else:
            photo_filename = ""
        print jid, photo_filename

if __name__ == '__main__':
    j = login('username', 'password')
    for x in range(30):
        j.Process(1) # process for 1 second
    display_filenames(j)
Advertisements

This one changes my status message on Google Talk

import xmpp

def set_status(username, password, status):
    jabber = xmpp.Client('gmail.com')
    jabber.connect(server=('talk.google.com',5223))
    jabber.auth(username, password, 'python_status_message')
    jabber.send(xmpp.Iq(typ='set', queryNS='google:shared-status',
        payload=[xmpp.Node('status', payload=status)]))

xmpp (jabber) photo module

I made a Google Talk and Google Maps mashup a while back. I could not find a xmpp module that gave access to the person’s picture, so I had to write one that extracts it from the vcard.

import base64, hashlib
from os import path
import xmpp

def photo_update_handler(session, stanza):
    JID = stanza['from'].getStripped()
    vupdate = stanza.getTag('x',namespace='vcard-temp:x:update')
    if not vupdate:return
    photo = vupdate.getTag('photo')
    if not photo:return
    photo = photo.getPayload()
    if not photo:return
    photo = photo[0]
    session._owner.Roster._data[JID]['photo'] = photo
    #download the photo only if we don't have a photo with that hash
    if not get_photo(photo) and 'summona' not in JID:
        #a quick fix to an unkonwn bug with andy's picture
        request_vcard(session, JID)

def register_handler(session):
    session.RegisterHandler(
        'presence',
        photo_update_handler,
        '',
        'jabber:client',
        )

def recieve_vcard(session, stanza):
    vcard = stanza.getTag('vCard')
    #name = vcard.getTags('FN')[0].getPayload()[0]
    photo = vcard.getTag('PHOTO')
    if not photo: return
    photo_type = photo.getTag('TYPE').getPayload()[0]
    photo_bin = photo.getTag('BINVAL').getPayload()[0]
    photo_bin = base64.b64decode(photo_bin)
    if photo_type == 'image/png':
        ext = '.png'
    elif photo_type == 'image/jpeg':
        ext = '.jpg'
    elif photo_type == 'image/gif':
        ext = '.gif'
    elif photo_type == 'image/bmp':
        ext = '.bmp'
    else:
        print "Unknown file type: %s" % photo_type
        ext = ''
    sha1sh = hashlib.sha1()
    sha1sh.update(photo_bin)
    sha1sh = sha1sh.hexdigest()
    f = file(sha1sh + ext,'wb')
    f.write(photo_bin)
    f.close()
    #JID = stanza['from'].getStripped()

def request_vcard(session, JID):
    n = xmpp.Node('vCard', attrs={'xmlns':xmpp.NS_VCARD})
    i = xmpp.Protocol('iq', JID, 'get', payload=[n])
    return session.SendAndCallForResponse(i, recieve_vcard)

def get_photo(sha1sh):
    ext = ['.jpg','.png','.gif','.bmp']
    for x in ext:
        if path.exists(sha1sh + x):
            return sha1sh + x