Module: MiniFB
- Defined in:
- lib/mini_fb.rb
Defined Under Namespace
Classes: FaceBookError, FaceBookSecret, GraphObject, OAuthSession, Photos, Session, User
Constant Summary collapse
- FB_URL =
Global constants
"http://api.facebook.com/restserver.php"
- FB_API_VERSION =
"1.0"
- BAD_JSON_METHODS =
["users.getloggedinuser", "auth.promotesession", "users.hasapppermission", "Auth.revokeExtendedPermission", "pages.isAdmin", "pages.isFan", "stream.publish", "dashboard.addNews", "dashboard.addGlobalNews", "dashboard.publishActivity", "dashboard.incrementcount", "dashboard.setcount" ].collect { |x| x.downcase }
- @@logging =
Define here the values for your application FB_URL = “api.facebook.com/restserver.php” FB_API_VERSION = “1.0”
false
Class Method Summary collapse
-
.call(api_key, secret, method, kwargs) ⇒ Object
The secret argument should be an instance of FacebookSecret to hide value from simple introspection.
- .disable_logging ⇒ Object
- .enable_logging ⇒ Object
- .fetch(url, options = {}) ⇒ Object
-
.fql(access_token, fql_query, options = {}) ⇒ Object
Executes an FQL query.
-
.get(access_token, id, options = {}) ⇒ Object
Gets data from the Facebook Graph API options: - type: eg: feed, home, etc - metadata: to include metadata in response.
- .graph_base ⇒ Object
-
.login_url(api_key, options = {}) ⇒ Object
Returns the login/add app url for your application.
-
.multifql(access_token, fql_queries, options = {}) ⇒ Object
Executes multiple FQL queries Example:.
-
.oauth_access_token(app_id, redirect_uri, secret, code) ⇒ Object
returns a hash with one value being ‘access_token’, the other being ‘expires’.
-
.oauth_url(app_id, redirect_uri, options = {}) ⇒ Object
options: - scope: comma separated list of extends permissions.
-
.parse_cookie_information(app_id, cookies) ⇒ Object
Parses cookies in order to extract the facebook cookie and parse it into a useable hash.
-
.post(access_token, id, options = {}) ⇒ Object
Posts data to the Facebook Graph API options: - type: eg: feed, home, etc - metadata: to include metadata in response.
- .post_upload(filename, kwargs) ⇒ Object
-
.rest(access_token, api_method, options = {}) ⇒ Object
Uses new Oauth 2 authentication against old Facebook REST API options: - params: Any additional parameters you would like to submit.
-
.scopes ⇒ Object
Returns all available scopes.
-
.validate(secret, arguments) ⇒ Object
DEPRECATED, use verify_signature instead.
-
.verify_connect_signature(api_key, secret, cookies) ⇒ Object
DEPRECATED: Please use
verify_cookie_signature
instead. -
.verify_cookie_signature(app_id, secret, cookies) ⇒ Object
Validates that the cookies sent by the user are those that were set by facebook.
-
.verify_signature(secret, arguments) ⇒ Object
Returns true is signature is valid, false otherwise.
Class Method Details
.call(api_key, secret, method, kwargs) ⇒ Object
The secret argument should be an instance of FacebookSecret to hide value from simple introspection.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/mini_fb.rb', line 158 def MiniFB.call(api_key, secret, method, kwargs) puts 'kwargs=' + kwargs.inspect if @@logging if secret.is_a? String secret = FaceBookSecret.new(secret) end # Prepare arguments for call call_id = kwargs.fetch("call_id", true) if call_id == true kwargs["call_id"] = Time.now.tv_sec.to_s else kwargs.delete("call_id") end custom_format = kwargs.include?("format") || kwargs.include?("callback") kwargs["format"] ||= "JSON" kwargs["v"] ||= FB_API_VERSION kwargs["api_key"]||= api_key kwargs["method"] ||= method file_name = kwargs.delete("filename") kwargs["sig"] = signature_for(kwargs, secret.value.call) fb_method = kwargs["method"].downcase if fb_method == "photos.upload" # Then we need a multipart post response = MiniFB.post_upload(file_name, kwargs) else begin response = Net::HTTP.post_form(URI.parse(FB_URL), post_params(kwargs)) rescue SocketError => err # why are we catching this and throwing as different error? hmmm.. # raise IOError.new( "Cannot connect to the facebook server: " + err ) raise err end end # Handle response return response.body if custom_format body = response.body puts 'response=' + body.inspect if @@logging begin data = JSON.parse(body) if data.include?("error_msg") raise FaceBookError.new(data["error_code"] || 1, data["error_msg"]) end rescue JSON::ParserError => ex if BAD_JSON_METHODS.include?(fb_method) # Little hack because this response isn't valid JSON if body == "0" || body == "false" return false end return body else raise ex end end return data end |
.disable_logging ⇒ Object
35 36 37 |
# File 'lib/mini_fb.rb', line 35 def self.disable_logging @@logging = false end |
.enable_logging ⇒ Object
31 32 33 |
# File 'lib/mini_fb.rb', line 31 def self.enable_logging @@logging = true end |
.fetch(url, options = {}) ⇒ Object
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 |
# File 'lib/mini_fb.rb', line 534 def self.fetch(url, ={}) begin if [:method] == :post puts 'url_post=' + url if @@logging resp = RestClient.post url, [:params] else if [:params] && [:params].size > 0 url += '?' + [:params].map { |k, v| URI.escape("%s=%s" % [k, v]) }.join('&') end puts 'url_get=' + url if @@logging resp = RestClient.get url end puts 'resp=' + resp.to_s if @@logging begin res_hash = JSON.parse(resp.to_s) rescue # quick fix for things like stream.publish that don't return json res_hash = JSON.parse("{\"response\": #{resp.to_s}}") end if res_hash.is_a? Array # fql return this res_hash.collect! { |x| Hashie::Mash.new(x) } else res_hash = Hashie::Mash.new(res_hash) end if res_hash.include?("error_msg") raise FaceBookError.new(res_hash["error_code"] || 1, res_hash["error_msg"]) end return res_hash rescue RestClient::Exception => ex puts ex.http_code.to_s puts 'ex.http_body=' + ex.http_body if @@logging res_hash = JSON.parse(ex.http_body) # probably should ensure it has a good response raise MiniFB::FaceBookError.new(ex.http_code, "#{res_hash["error"]["type"]}: #{res_hash["error"]["message"]}") end end |
.fql(access_token, fql_query, options = {}) ⇒ Object
Executes an FQL query
494 495 496 497 498 499 500 501 502 503 |
# File 'lib/mini_fb.rb', line 494 def self.fql(access_token, fql_query, ={}) url = "https://api.facebook.com/method/fql.query" params = [:params] || {} params["access_token"] = "#{(access_token)}" params["metadata"] = "1" if [:metadata] params["query"] = fql_query params["format"] = "JSON" [:params] = params return fetch(url, ) end |
.get(access_token, id, options = {}) ⇒ Object
Gets data from the Facebook Graph API options:
- type: eg: feed, home, etc
- metadata: to include metadata in response. true/false
- params: Any additional parameters you would like to submit
458 459 460 461 462 463 464 465 466 |
# File 'lib/mini_fb.rb', line 458 def self.get(access_token, id, ={}) url = "#{graph_base}#{id}" url << "/#{[:type]}" if [:type] params = [:params] || {} params["access_token"] = "#{(access_token)}" params["metadata"] = "1" if [:metadata] [:params] = params return fetch(url, ) end |
.graph_base ⇒ Object
420 421 422 |
# File 'lib/mini_fb.rb', line 420 def self.graph_base "https://graph.facebook.com/" end |
.login_url(api_key, options = {}) ⇒ Object
Returns the login/add app url for your application.
options:
- :next => a relative next page to go to. relative to your facebook connect url or if :canvas is true, then relative to facebook app url
- :canvas => true/false - to say whether this is a canvas app or not
312 313 314 315 316 317 |
# File 'lib/mini_fb.rb', line 312 def self.login_url(api_key, ={}) login_url = "http://api.facebook.com/login.php?api_key=#{api_key}" login_url << "&next=#{[:next]}" if [:next] login_url << "&canvas" if [:canvas] login_url end |
.multifql(access_token, fql_queries, options = {}) ⇒ Object
Executes multiple FQL queries Example:
MiniFB.multifql(access_token, { :statuses => “SELECT status_id, message FROM status WHERE uid = 12345”,
:privacy => "SELECT object_id, description FROM privacy WHERE object_id IN (SELECT status_id FROM #statuses)" })
510 511 512 513 514 515 516 517 518 519 |
# File 'lib/mini_fb.rb', line 510 def self.multifql(access_token, fql_queries, ={}) url = "https://api.facebook.com/method/fql.multiquery" params = [:params] || {} params["access_token"] = "#{(access_token)}" params["metadata"] = "1" if [:metadata] params["queries"] = JSON[fql_queries] params[:format] = "JSON" [:params] = params return fetch(url, ) end |
.oauth_access_token(app_id, redirect_uri, secret, code) ⇒ Object
returns a hash with one value being ‘access_token’, the other being ‘expires’
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/mini_fb.rb', line 436 def self.oauth_access_token(app_id, redirect_uri, secret, code) oauth_url = "#{graph_base}oauth/access_token" oauth_url << "?client_id=#{app_id}" oauth_url << "&redirect_uri=#{URI.escape(redirect_uri)}" oauth_url << "&client_secret=#{secret}" oauth_url << "&code=#{URI.escape(code)}" resp = RestClient.get oauth_url puts 'resp=' + resp.body.to_s if @@logging params = {} params_array = resp.split("&") params_array.each do |p| ps = p.split("=") params[ps[0]] = ps[1] end return params end |
.oauth_url(app_id, redirect_uri, options = {}) ⇒ Object
options:
- scope: comma separated list of extends permissions. see http://developers.facebook.com/docs/authentication/permissions
426 427 428 429 430 431 432 433 |
# File 'lib/mini_fb.rb', line 426 def self.oauth_url(app_id, redirect_uri, ={}) oauth_url = "#{graph_base}oauth/authorize" oauth_url << "?client_id=#{app_id}" oauth_url << "&redirect_uri=#{URI.escape(redirect_uri)}" # oauth_url << "&scope=#{options[:scope]}" if options[:scope] oauth_url << ("&" + .map { |k, v| "%s=%s" % [k, v] }.join('&')) unless .empty? oauth_url end |
.parse_cookie_information(app_id, cookies) ⇒ Object
Parses cookies in order to extract the facebook cookie and parse it into a useable hash
options:
-
app_id - the connect applications app_id (some users may find they have to use their facebook API key)
-
secret - the connect application secret
-
cookies - the cookies given by facebook - it is ok to just pass all of the cookies, the method will do the filtering for you.
281 282 283 284 |
# File 'lib/mini_fb.rb', line 281 def MiniFB.(app_id, ) return nil if ["fbs_#{app_id}"].nil? Hash[*["fbs_#{app_id}"].split('&').map { |v| v.gsub('"', '').split('=', 2) }.flatten] end |
.post(access_token, id, options = {}) ⇒ Object
Posts data to the Facebook Graph API options:
- type: eg: feed, home, etc
- metadata: to include metadata in response. true/false
- params: Any additional parameters you would like to submit
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/mini_fb.rb', line 473 def self.post(access_token, id, ={}) url = "#{graph_base}#{id}" url << "/#{[:type]}" if [:type] .delete(:type) params = [:params] || {} .each do |key, value| if value.kind_of?(File) params[key] = value else params[key] = "#{value}" end end params["access_token"] = "#{(access_token)}" params["metadata"] = "1" if [:metadata] [:params] = params [:method] = :post return fetch(url, ) end |
.post_upload(filename, kwargs) ⇒ Object
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/mini_fb.rb', line 224 def MiniFB.post_upload(filename, kwargs) content = File.open(filename, 'rb') { |f| f.read } boundary = Digest::MD5.hexdigest(content) header = {'Content-type' => "multipart/form-data, boundary=#{boundary}"} # Build query query = '' kwargs.each { |a, v| query << "--#{boundary}\r\n" << "Content-Disposition: form-data; name=\"#{a}\"\r\n\r\n" << "#{v}\r\n" } query << "--#{boundary}\r\n" << "Content-Disposition: form-data; filename=\"#{File.basename(filename)}\"\r\n" << "Content-Transfer-Encoding: binary\r\n" << "Content-Type: image/jpeg\r\n\r\n" << content << "\r\n" << "--#{boundary}--" # Call Facebook with POST multipart/form-data request uri = URI.parse(FB_URL) Net::HTTP.start(uri.host) { |http| http.post uri.path, query, header } end |
.rest(access_token, api_method, options = {}) ⇒ Object
Uses new Oauth 2 authentication against old Facebook REST API options:
- params: Any additional parameters you would like to submit
524 525 526 527 528 529 530 531 |
# File 'lib/mini_fb.rb', line 524 def self.rest(access_token, api_method, ={}) url = "https://api.facebook.com/method/#{api_method}" params = [:params] || {} params[:access_token] = access_token params[:format] = "JSON" [:params] = params return fetch(url, ) end |
.scopes ⇒ Object
Returns all available scopes.
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
# File 'lib/mini_fb.rb', line 578 def self.scopes scopes = %w{ about_me activities birthday education_history events groups hometown interests likes location notes online_presence photo_video_tags photos relationships religion_politics status videos website work_history } scopes.map! do |scope| ["user_#{scope}", "friends_#{scope}"] end.flatten! scopes += %w{ read_insights read_stream read_mailbox read_friendlists read_requests email ads_management xmpp_login publish_stream create_event rsvp_event sms offline_access } end |
.validate(secret, arguments) ⇒ Object
DEPRECATED, use verify_signature instead
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
# File 'lib/mini_fb.rb', line 614 def MiniFB.validate(secret, arguments) signature = arguments.delete("fb_sig") return arguments if signature.nil? unsigned = Hash.new signed = Hash.new arguments.each do |k, v| if k =~ /^fb_sig_(.*)/ then signed[$1] = v else unsigned[k] = v end end arg_string = String.new signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] } if Digest::MD5.hexdigest(arg_string + secret) != signature unsigned # Hash is incorrect, return only unsigned fields. else unsigned.merge signed end end |
.verify_connect_signature(api_key, secret, cookies) ⇒ Object
DEPRECATED: Please use verify_cookie_signature
instead.
302 303 304 305 |
# File 'lib/mini_fb.rb', line 302 def MiniFB.verify_connect_signature(api_key, secret, ) warn "DEPRECATION WARNING: 'verify_connect_signature' has been renamed to 'verify_cookie_signature' as Facebook no longer calls this 'connect'" MiniFB.(api_key, secret, ) end |
.verify_cookie_signature(app_id, secret, cookies) ⇒ Object
Validates that the cookies sent by the user are those that were set by facebook. Since your secret is only known by you and facebook it is used to sign all of the cookies set.
options:
-
app_id - the connect applications app_id (some users may find they have to use their facebook API key)
-
secret - the connect application secret
-
cookies - the cookies given by facebook - it is ok to just pass all of the cookies, the method will do the filtering for you.
293 294 295 296 297 298 299 |
# File 'lib/mini_fb.rb', line 293 def MiniFB.(app_id, secret, ) fb_keys = MiniFB.(app_id, ) return false if fb_keys.nil? signature = fb_keys.delete('sig') return signature == Digest::MD5.hexdigest(fb_keys.map { |k, v| "#{k}=#{v}" }.sort.join + secret) end |
.verify_signature(secret, arguments) ⇒ Object
Returns true is signature is valid, false otherwise.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/mini_fb.rb', line 252 def MiniFB.verify_signature(secret, arguments) signature = arguments.delete("fb_sig") return false if signature.nil? unsigned = Hash.new signed = Hash.new arguments.each do |k, v| if k =~ /^fb_sig_(.*)/ then signed[$1] = v else unsigned[k] = v end end arg_string = String.new signed.sort.each { |kv| arg_string << kv[0] << "=" << kv[1] } if Digest::MD5.hexdigest(arg_string + secret) == signature return true end return false end |