DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Setting An Avatar/Buddy Icon With Ruby's XMPP4R API

06.17.2008
| 5075 views |
  • submit to reddit
        I have been making service bots using Ruby, XMPP4R, and Jabber for some time now. I recently had someone ask if I could add a buddy icon to one of them. I had a hard time finding info on the net about doing this with XMPP4R.

After some trial and error and RFC reading, I got it to work. Here is a skeleton code listing of what I did to get it to work. This is also detailed at: http://nocompile.blogspot.com/2008/06/setting-avatar-with-rubys-xmpp4r-api.html

require 'rubygems'
require 'xmpp4r'
# allows me to use the vcard stuff
require 'xmpp4r/vcard'
# for encoding the buddy icon
require 'base64'

class JabberBot
  # user : Jabber ID
  # pass : Password
  def initialize(user, pass)
    # no debugging for now
    Jabber::debug = false
 
    # connect Jabber client to the Server
    @client = Jabber::Client.new(Jabber::JID.new(user))
    @client.connect
    @client.auth(pass)
    # send initial presence to let the server know you are ready for messages
    @client.send(Jabber::Presence::new)

    # set vcard info
    # this is in a thread because it waits on a server response
    # the XMPP4R docs suggest placing this code inside a thread
    avatar_sha1 = nil # this gets used later on
    Thread.new do
      vcard = Jabber::Vcard::IqVcard.new
      vcard["FN"] = "My Bot" # full name
      vcard["NICKNAME"] = "mybot" # nickname
      # buddy icon stuff
      vcard["PHOTO/TYPE"] = "image/png"
      # open buddy icon/avatar image file
      image_file = File.new("buddy.png", "r")
      # Base64 encode the file contents
      image_b64 = Base64.b64encode(image_file.read())
      # process sha1 hash of photo contents
      # this is used by the presence setting
      image_file.rewind # must rewind the file to the beginning
      avatar_sha1 = Digest::SHA1.hexdigest(image_file.read())
      vcard["PHOTO/BINVAL"] = image_b64 # set the BINVAL to the Base64 encoded contents of our image
      begin
        # create a vcard helper and immediately set the vcard info
        vcard_helper = Jabber::Vcard::Helper.new(@client).set(vcard)
      rescue
        # very simple error "logging" if you can call it that
        puts "#{Time.now} vcard operation timed out." 
      end
    end
   
    # just a 'keepalive' thread to keep the jabber server in touch
    # sends a presence entity every 30 seconds
    Thread.new do
      while true do
        # saying "I'm alive!" to the Jabber server
        pres = Jabber::Presence::new
        
        # according to the RFC the server expects a SHA1 hash of the avatar to be 
        # sent with subsequent presence messages
        #
        # it wants it in the following format:
        #
        # <presence from='username@jabber.server/ResourceName'>
        #    <x xmlns='vcard-temp:x:update'>
        #        <photo>sha1-hash-of-image</photo>
        #    </x>
        # </presence>       
        #
        # append buddy icon/avatar info
        if not avatar_sha1.nil?
            # send the sha1 hash of the avatar to the server
            # as per the RFC http://www.xmpp.org/extensions/xep-0153.html
            x = REXML::Element::new("x")
            x.add_namespace('vcard-temp:x:update')
            photo = REXML::Element::new("photo")
            # this is the avatar hash as computed in the vcard thread above
            avatar_hash = REXML::Text.new(avatar_sha1)
            # add text to photo
            photo.add(avatar_hash)
            # add photo to x
            x.add(photo)
            # add x to presence
            pres.add_element(x)
        end
        # send presence entity
        @client.send(pres)
        sleep 30
    end # end 'keepalive'
  end # initialize
end

# the most barebones basic way to run this code
bot = JabberBot.new('username@jabber.server', 'password')

while true do
end