Updating Facebook Profiles and Feeds with RFacebook

One of the things I found difficult to understand when it comes to writing a Facebook application is how to add a “dynamic” panel to a users’ profile page, and how to auto-add entries to their mini-feed. A couple of examples include:

  • Putting “Nathan has added 5 photos to Flickr” or “Nathan is listening to Computer Camp Love by Datarock” on my mini-feed.
  • Adding a dynamic panel to my profile which shows the last 10 patches I have submitted to the Ruby on Rails project.

It turns out this is a fairly trivial exercise with Matt Pizzimenti’s RFacebook gem. The following is a quick run-down of what’s required for adding content to the profiles of users who have installed your (Rails based) Facebook application via a push strategy. I’ve done a gem unpack of hpricot-0.5, json-1.1.0 and rfacebook-0.6.2 to RAILS_ROOT/vendor/plugins.

The “Infinite Session”

In order for you to be able to add content independently of a Facebook user being logged in, you need to have a way of authenticating yourself. When a user is browsing your application through the Facebook interface, the RFacebook library gives you access to the fbsession variable which represents the user who is currently logged into Facebook. The problem is, this session will expire. You can find out when a session will expire by checking the value of fbsession.session_expires.

But surely we don’t want to force a user to log in to Facebook every time we want to update their profile with notifications / new content from our third-party application? So how do we get a session that doesn’t expire? The trick is providing a link to the following URL:

http://www.facebook.com/code_gen.php?v=1.0&api_key=YOUR_API_KEY

Once the user clicks the “Generate” button on that page, every time you call auth.getSession, you’ll get an identical session ID which has a fbsession.session_expires value of 0. While the user is interacting with your Facebook application, you should serialise this to some kind of storage such as the ActiveRecord model of your user so you can use it later:

Create a table to store user IDs & their infinite session token
1
2
3
4
5
6
7
8
9
10
11
class FacebookUser < ActiveRecord::Migration
  def self.up
    create_table :facebook_users do |t|
      t.column :user_id, :string, :null => false
      t.column :infinite_session, :null => false
    end
  end
  def self.down
    drop_table :facebook_users
  end
end

And the sample controller:

Rails controller which saves the infinite session
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require 'facebook_rails_controller_extensions'
class FacebookController < ApplicationController
  include RFacebook::RailsControllerExtensions

  FB_APP_URL = 'http://apps.facebook.com/my-app-name'

  before_filter :require_facebook_login
  before_filter :require_configuration, :except => :configure

  def facebook_api_key() 'YOUR_API_KEY'; end
  def facebook_api_secret() 'YOUR_API_SECRET'; end
  def finish_facebook_login() redirect_to FB_APP_URL; end

  def require_configuration
    @user = FacebookUser.find_by_user_id(fbsession.session_user_id)
    redirect_to :action => :configure unless @user
  end

  def configure
    @user = FacebookUser.find_or_initialize_by_user_id(fbsession.session_user_id)
    unless fbsession.session_expires
      @user.infinite_session = fbsession.session_key
      @user.save!
    end
  end
end

This means we can now run a little script (via cron, if you wish):

Script to update a user’s profile using their infinite session
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env /path/to/application/script/runner

require 'facebook_rails_controller_extensions'

session = RFacebook::FacebookWebSession.new('YOUR_API_KEY', 'YOUR_API_SECRET')
FacebookUser.find(:all).each do |user|
  session.activate_with_previous_session(user.infinite_session, user.user_id)
  session.profile_setFBML(:uid => user.user_id, :markup => 'My really dynamic content')
  session.feed_publishActionOfUser(:title => 'added some really dynamic content')
end

You could run this script every 5 minutes, or perhaps even directly call session.feed_publishActionOfUser and session.profile_setFBML from your application when the user does something (such as creating a resource).