{{ !articles[0].partner.isSponsoringArticle ? "Platinum" : "Portal" }} Partner

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

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))
    # send initial presence to let the server know you are ready for messages

    # 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
        # create a vcard helper and immediately set the vcard info
        vcard_helper = Jabber::Vcard::Helper.new(@client).set(vcard)
        # very simple error "logging" if you can call it that
        puts "#{Time.now} vcard operation timed out." 
    # 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:
        #        sha1-hash-of-image
        # 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")
            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
            # add photo to x
            # add x to presence
        # send presence entity
        sleep 30
    end # end 'keepalive'
  end # initialize

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

while true do
{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks