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

11 thoughts on “XMPP Jabber Photo Module”

  1. Hey there,
    Is there an easy way to use this code to update a buddy list of avatars for each buddy?
    I am a newbie to XMPP.py and am just wondering how I could understand exactly what your code does and how to use it.
    Thanks,
    Needz

    Like

  2. Thanks for the update, what your doing makes a lot more sense now. The only thing is when I try to run the code, after creating the appropriate directory, I get the following error:

    Traceback (most recent call last):
    File “C:\Users\Nida\Desktop\usePhotos.py”, line 40, in
    j.Process(1) # process for 1 second
    File “C:\Python26\lib\site-packages\xmpppy-0.5.0rc1-py2.6.egg\xmpp\dispatcher.py”, line 122, in Process
    self.Stream.Parse(data)
    File “C:\Python26\lib\site-packages\xmpppy-0.5.0rc1-py2.6.egg\xmpp\simplexml.py”, line 424, in endtag
    self.dispatch(self._mini_dom)
    File “C:\Python26\lib\site-packages\xmpppy-0.5.0rc1-py2.6.egg\xmpp\dispatcher.py”, line 293, in dispatch
    try: cb(session,stanza,**args)
    File “C:\Users\Nida\Desktop\photo.py”, line 57, in recieve_vcard
    photo = stanza.getTag(‘vCard’).getTag(‘PHOTO’)
    AttributeError: ‘NoneType’ object has no attribute ‘getTag’

    I am not sure which ‘getTag’ it is referencing! Do you think it has something do to with there being no vcard for a certain contact?

    Like

  3. Actually, when I use a jabber account to log in instead of a gmail account, it works just fine! Success. I guess there is something fishy with gmail!

    Thank-you so much for the code, it was very helpful!

    Like

  4. Thank you for that, it now works great for gmail and regular jabber accounts!

    Do you know if gmail supports Vcard updates?
    What I mean to say is, if I have code that updates a persons Vcard with a photo, will it work for gmail too? Because so far I am having issues with it. It works fine for jabber but not gmail. So I thought I’d ask if you’ve ever given it a go?

    Like

  5. Thanks,
    I have managed to get that working, I do something similar to the website you suggested.
    I am wondering if you have used your code to get your own avatar?
    I am trying something like the following:
    “””
    class MyAvatar:
    def __init__(self, presence, client, jid):
    self.jid_photo_map = {}
    self.presence = presence
    self.client = client
    self.jid = xmpp.protocol.JID(jid).getStripped()

    def retrievePhoto(self, session, stanza):
    vphoto = self.presence.getTag(‘x’, namespace = ‘vcard-temp:x:update’)
    if not vphoto:
    return
    photo = vphoto.getTag(‘photo’)
    if not photo:
    return
    photo = photo.getData()
    if not photo:
    return
    self.jid_photo_map[self.jid] = photo

    def loggedIn(self):
    self.client.RegisterHandler(‘presence’, self.retrievePhoto)
    photo.register_handler(self.client)
    return self.client

    def display(self, client):
    photo_filename = photo.get_photo(self.jid_photo_map[self.jid])
    print self.jid, photo_filename

    def getMyPhoto(self):
    j = self.loggedIn()
    for x in range(30):
    j.Process(1)
    self.display(j)
    “””
    I can’t quite get it right. I tired to copy the same way you get other’s avatars, but this seems quite wrong!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s