Home » Facebook應用程式, Featured

[Facebook開發] 如何時時追蹤安裝/退出你的應用程式的使用者?

Sting Tao 16 January 2010 613 次閱讀 View Comments

看到Robert Shedd的這篇部落格,提到這個問題,整理一下分享給大家,相關的code也是引用自該文章。

基本問題 – 因為facebook沒給安裝你應用的用戶清單,沒辦法精準掌握現有用戶

這裡假設你已經用RoR+Facebooker開發了幾個應用,如果還沒,可以閱讀:

那麼,用facebooker開發好了一個ROR在facebook上的應用後,如果想知道目前某人現在是否安裝了你的程式,當他連上有建立Session的時候,我們用User#has_added_app可以知道他是否安裝了程式。如果把他的user id 存在local的資料庫裡,那麼你就可以知道目前所有"安裝了"你應用程式的使用者了。

不過,如果使用者在某個時間刪除掉了你的應用程式,也就是說他退出了,由於退出時沒有建立與ap間的session,我們不能用has_added_app來了解狀況,並刪除local資料庫的資料。使用者有進有出,時間久了,你的local資料庫上的資料就不準確了。若有做排名的列表,就可能出現幽靈人,明明已經沒裝你的應用,卻仍高掛第一名。

解決方法 – 使用 post_authorize與 post_deauthorize

針對這樣的需求,facebook提供了post_authorize與post_deauthorize兩個callback,註冊了應用跟移除應用都可以通知我們的ap一聲,在這個地方做些事情,那麼我們就能確保數據的一致性了。

首先,Robert建議另開一個Controller,另外就是要注意要確認authenticity token,程式碼範例如下:

class CallbacksController < ApplicationController
  skip_before_filter :ensure_app_installed, :except => :post_authorize
  skip_before_filter :verify_authenticity_token

  def post_authorize
    if request.post?
      #做些事情,例如存資料庫
      render :nothing => true
    end
  end

  def post_deauthorize
    if request.post?
      #做些事情,例如刪除資料
      render :nothing => true
    end
  end

end

進階注意-進一步確認加入/退出請求實際來自Facebook平台
以上程式碼已經可以了,可以解決使用者新增與退出應用時,我們能精準掌握他的狀態的問題了。不過,我們仍得注意另一個問題,就是這個動作是否來自facebook平台,免得被其他人透過其他方式觸發了。在上述的程式碼裡加上這段檢查,就變成了:

class CallbacksController < ApplicationController
  skip_before_filter :ensure_app_installed, :except => :post_authorize
  skip_before_filter :verify_authenticity_token

  def post_authorize
    if request.post?
      if verify_uninstall_signature
        #將使用者的has_app設成1
      end
    end
    render :nothing => true
  end

  def post_deauthorize
    if request.post?
      if verify_uninstall_signature
        #把使用者的has_app設成0
      end
    end
    render :nothing => true
  end

  private
      #參考了 http://wiki.developers.facebook.com/index.php/Post-Remove_URL
      def verify_uninstall_signature
        signature = ''
        keys = params.keys.sort
        keys.each do |key|
          next if key == 'fb_sig'
          next unless key.include?('fb_sig')
          key_name = key.gsub('fb_sig_', '')
          signature += key_name
          signature += '='
          signature += params[key]
        end

        signature += FACEBOOK_YAML['secret_key']
        calculated_sig = Digest::MD5.hexdigest(signature)

        if calculated_sig != params[:fb_sig]
          logger.warn "\nWARNING :: potential spoofing :: expected signatures did not match"
          logger.info "\nSignature (fb_sig param from facebook) :: #{params[:fb_sig]}"
          logger.info "\nSignature String (pre-hash) :: #{signature}"
          logger.info "\nMD5 Hashed Sig :: #{calculated_sig}"

          #check to see if ip variables are nil
          if not request.env['HTTP_X_FORWARDED_FOR'].nil? and not request.env['HTTP_X_REAL_IP'].nil?
            ip = request.env['HTTP_X_FORWARDED_FOR'] || request.env['HTTP_X_REAL_IP']
          else
            ip = request.remote_ip
          end

          logger.info "\nRemote IP :: #{ip}"
          return false
        else
          #logger.warn "\n\nSUCCESS!! Signatures matched.\n"
        end
        return true
      end

end

其中要注意的是關於FACEBOOK_YAML[‘secret_key’]的這行,密碼金鑰要定義在environment.rb:

    #load facebooker configuration for usage
    facebook_config = File.join(RAILS_ROOT, 'config', 'facebooker.yml')
    FACEBOOK_YAML = YAML::load(ERB.new(File.read(facebook_config)).result)[RAILS_ENV]

這樣,就可以準確掌握你的應用程式中真正的活躍者了!Happy Coding!

Related Posts with Thumbnails
Line Break

關於作者: Sting Tao (90 篇文章)

陶韻智,待過IBM與NHN。 關注數位趨勢、網路發展與攀岩。一半的時間在台北,一半的時間在矽谷。 歡迎來信 stingtao@gmail.com

blog comments powered by Disqus