source: perl/modules/Facebook/lib/Facebook/Graph.pm @ cfca761

Last change on this file since cfca761 was cfca761, checked in by Edward Z. Yang <ezyang@mit.edu>, 13 years ago
Add vanilla Facebook::Graph. Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
  • Property mode set to 100644
File size: 14.9 KB
Line 
1package Facebook::Graph;
2BEGIN {
3  $Facebook::Graph::VERSION = '1.0300';
4}
5
6use Any::Moose;
7use MIME::Base64::URLSafe;
8use JSON;
9use Facebook::Graph::AccessToken;
10use Facebook::Graph::Authorize;
11use Facebook::Graph::Query;
12use Facebook::Graph::Picture;
13use Facebook::Graph::Publish::Post;
14use Facebook::Graph::Publish::Checkin;
15use Facebook::Graph::Publish::Like;
16use Facebook::Graph::Publish::Comment;
17use Facebook::Graph::Publish::Note;
18use Facebook::Graph::Publish::Link;
19use Facebook::Graph::Publish::Event;
20use Facebook::Graph::Publish::RSVPMaybe;
21use Facebook::Graph::Publish::RSVPAttending;
22use Facebook::Graph::Publish::RSVPDeclined;
23use Ouch;
24
25has app_id => (
26    is      => 'ro',
27);
28
29has secret => (
30    is          => 'ro',
31    predicate   => 'has_secret',
32);
33
34has postback => (
35    is      => 'ro',
36);
37
38has access_token => (
39    is          => 'rw',
40    predicate   => 'has_access_token',
41);
42
43
44sub parse_signed_request {
45    my ($self, $signed_request) = @_;
46    require Digest::SHA;
47    my ($encoded_sig, $payload) = split(/\./, $signed_request);
48
49        my $sig = urlsafe_b64decode($encoded_sig);
50    my $data = JSON->new->decode(urlsafe_b64decode($payload));
51
52    if (uc($data->{'algorithm'}) ne "HMAC-SHA256") {
53        ouch '500', 'Unknown algorithm. Expected HMAC-SHA256';
54    }
55
56    my $expected_sig = Digest::SHA::hmac_sha256($payload, $self->secret);
57    if ($sig ne $expected_sig) {
58        ouch '500', 'Bad Signed JSON signature!';
59    }
60    return $data;
61}
62
63sub request_access_token {
64    my ($self, $code) = @_;
65    my $token = Facebook::Graph::AccessToken->new(
66        code            => $code,
67        postback        => $self->postback,
68        secret          => $self->secret,
69        app_id          => $self->app_id,
70    )->request;
71    $self->access_token($token->token);
72    return $token;
73}
74
75sub convert_sessions {
76    my ($self, $sessions) = @_;
77    return Facebook::Graph::Session->new(
78        secret          => $self->secret,
79        app_id          => $self->app_id,
80        sessions        => $sessions,
81        )
82        ->request
83        ->as_hashref;
84}
85
86sub authorize { 
87    my ($self) = @_;
88    return Facebook::Graph::Authorize->new(
89        app_id          => $self->app_id,
90        postback        => $self->postback,
91    );
92}
93
94sub fetch {
95    my ($self, $object_name) = @_;
96    return $self->query->find($object_name)->request->as_hashref;
97}
98
99sub query {
100    my ($self) = @_;
101    my %params;
102    if ($self->has_access_token) {
103        $params{access_token} = $self->access_token;
104    }
105    if ($self->has_secret) {
106        $params{secret} = $self->secret;
107    }
108    return Facebook::Graph::Query->new(%params);
109}
110
111sub picture {
112    my ($self, $object_name) = @_;
113    return Facebook::Graph::Picture->new( object_name => $object_name );
114}
115
116sub add_post {
117    my ($self, $object_name) = @_;
118    my %params;
119    if ($object_name) {
120        $params{object_name} = $object_name;
121    }
122    if ($self->has_access_token) {
123        $params{access_token} = $self->access_token;
124    }
125    if ($self->has_secret) {
126        $params{secret} = $self->secret;
127    }
128    return Facebook::Graph::Publish::Post->new( %params );
129}
130
131sub add_checkin {
132    my ($self, $object_name) = @_;
133    my %params;
134    if ($object_name) {
135        $params{object_name} = $object_name;
136    }
137    if ($self->has_access_token) {
138        $params{access_token} = $self->access_token;
139    }
140    if ($self->has_secret) {
141        $params{secret} = $self->secret;
142    }
143    return Facebook::Graph::Publish::Checkin->new( %params );
144}
145
146sub add_like {
147    my ($self, $object_name) = @_;
148    my %params = (
149        object_name => $object_name,
150    );
151    if ($self->has_access_token) {
152        $params{access_token} = $self->access_token;
153    }
154    if ($self->has_secret) {
155        $params{secret} = $self->secret;
156    }
157    return Facebook::Graph::Publish::Like->new( %params );
158}
159
160sub add_comment {
161    my ($self, $object_name) = @_;
162    my %params = (
163        object_name => $object_name,
164    );
165    if ($self->has_access_token) {
166        $params{access_token} = $self->access_token;
167    }
168    if ($self->has_secret) {
169        $params{secret} = $self->secret;
170    }
171    return Facebook::Graph::Publish::Comment->new( %params );
172}
173
174sub add_note {
175    my ($self) = @_;
176    my %params;
177    if ($self->has_access_token) {
178        $params{access_token} = $self->access_token;
179    }
180    if ($self->has_secret) {
181        $params{secret} = $self->secret;
182    }
183    return Facebook::Graph::Publish::Note->new( %params );
184}
185
186sub add_link {
187    my ($self) = @_;
188    my %params;
189    if ($self->has_access_token) {
190        $params{access_token} = $self->access_token;
191    }
192    if ($self->has_secret) {
193        $params{secret} = $self->secret;
194    }
195    return Facebook::Graph::Publish::Link->new( %params );
196}
197
198sub add_event {
199    my ($self, $object_name) = @_;
200    my %params;
201    if ($object_name) {
202        $params{object_name} = $object_name;
203    }
204    if ($self->has_access_token) {
205        $params{access_token} = $self->access_token;
206    }
207    if ($self->has_secret) {
208        $params{secret} = $self->secret;
209    }
210    return Facebook::Graph::Publish::Event->new( %params );
211}
212
213sub rsvp_maybe {
214    my ($self, $object_name) = @_;
215    my %params = (
216        object_name => $object_name,
217    );
218    if ($self->has_access_token) {
219        $params{access_token} = $self->access_token;
220    }
221    if ($self->has_secret) {
222        $params{secret} = $self->secret;
223    }
224    return Facebook::Graph::Publish::RSVPMaybe->new( %params );
225}
226
227sub rsvp_attending {
228    my ($self, $object_name) = @_;
229    my %params = (
230        object_name => $object_name,
231    );
232    if ($self->has_access_token) {
233        $params{access_token} = $self->access_token;
234    }
235    if ($self->has_secret) {
236        $params{secret} = $self->secret;
237    }
238    return Facebook::Graph::Publish::RSVPAttending->new( %params );
239}
240
241sub rsvp_declined {
242    my ($self, $object_name) = @_;
243    my %params = (
244        object_name => $object_name,
245    );
246    if ($self->has_access_token) {
247        $params{access_token} = $self->access_token;
248    }
249    if ($self->has_secret) {
250        $params{secret} = $self->secret;
251    }
252    return Facebook::Graph::Publish::RSVPDeclined->new( %params );
253}
254
255
256
257no Any::Moose;
258__PACKAGE__->meta->make_immutable;
259
260=head1 NAME
261
262Facebook::Graph - A fast and easy way to integrate your apps with Facebook.
263
264=head1 VERSION
265
266version 1.0300
267
268=head1 SYNOPSIS
269
270 my $fb = Facebook::Graph->new;
271 my $sarah_bownds = $fb->fetch('sarahbownds');
272 my $perl_page = $fb->fetch('16665510298');
273 
274Or better yet:
275
276 my $sarah_bownds = $fb->query
277    ->find('sarahbownds')
278    ->include_metadata
279    ->select_fields(qw( id name picture ))
280    ->request
281    ->as_hashref;
282   
283 my $sarahs_picture_uri = $fb->picture('sarahbownds')->get_large->uri_as_string;
284
285Or fetching a response from a URI you already have:
286
287 my $response = $fb->query
288    ->request('https://graph.facebook.com/btaylor')
289    ->as_hashref;
290 
291 
292=head2 Building A Privileged App
293
294 my $fb = Facebook::Graph->new(
295    app_id          => $facebook_application_id,
296    secret          => $facebook_application_secret,
297    postback        => 'https://www.yourapplication.com/facebook/oauth/postback',
298 );
299
300Get the user to authorize your app (only needed if you want to fetch non-public information or publish stuff):
301
302 my $uri = $fb
303    ->authorize
304    ->extend_permissions(qw(offline_access publish_stream))
305    ->uri_as_string;
306
307 # redirect the user's browser to $uri
308
309Handle the Facebook authorization code postback:
310
311 my $q = Plack::Request->new($env);
312 $fb->request_access_token($q->query_param('code'));
313 
314Or if you already had the access token:
315
316 $fb->access_token($token);
317 
318Get some info:
319
320 my $user = $fb->fetch('me');
321 my $friends = $fb->fetch('me/friends');
322 my $sarah_bownds = $fb->fetch('sarahbownds');
323
324=head1 DESCRIPTION
325
326This is a Perl interface to the Facebook Graph API L<http://developers.facebook.com/docs/api>. With this module you can currently query public Facebook data, query privileged Facebook data, and build a privileged Facebook application. See the TODO for all that this module cannot yet do.
327
328For example code, see L<Facebook::Graph::Cookbook>.
329
330B<WARNING:> The work on this module has only just begun because the Graph API itself isn't very new, and I'm only working on it as I have some tuits. Therefore things are potentially subject to change drastically with each release.
331
332
333=head1 METHODS
334
335=head2 new ( [ params ] )
336
337The constructor.
338
339=head3 params
340
341A hash of base parameters, just so you don't have to pass them around. If you only want to do public queries then these params are not needed.
342
343=over
344
345=item access_token
346
347An access token string used to make Facebook requests as a privileged user. Required if you want to make privileged queries or perform privileged actions on Facebook objects.
348
349=item app_id
350
351The application id that you get from Facebook after registering (L<http://developers.facebook.com/setup/>) your application on their site. Required if you'll be calling the C<request_access_token>, C<convert_sessions>, or C<authorize> methods.
352
353=item secret
354
355The application secret that you get from Facebook after registering your application. Required if you'll be calling the C<request_access_token> or C<convert_sessions> methods.
356
357=item postback
358
359The URI that Facebook should post your authorization code back to. Required if you'll be calling the C<request_access_token> or C<authorize> methods.
360
361B<NOTE:> It must be a sub URI of the URI that you put in the Application Settings > Connect > Connect URL field of your application's profile on Facebook.
362
363=back
364
365
366=head2 authorize ( )
367
368Creates a L<Facebook::Graph::Authorize> object, which can be used to get permissions from a user for your application.
369
370
371=head2 request_access_token ( code )
372
373Creates a L<Facebook::Graph::AccessToken> object and fetches an access token from Facebook, which will allow everything you do with Facebook::Graph to work within user privileges rather than through the public interface. Returns a L<Facebook::Graph::AccessToken::Response> object, and also sets the C<access_token> property in the Facebook::Graph object.
374
375=head3 code
376
377An authorization code string that you should have gotten by going through the C<authorize> process.
378
379
380=head2 query ( )
381
382Creates a L<Facebook::Graph::Query> object, which can be used to fetch and search data from Facebook.
383
384
385=head2 fetch ( id )
386
387Returns a hash reference of an object from facebook. A quick way to grab an object from Facebook. These two statements are identical:
388
389 my $sarah = $fb->fetch('sarahbownds');
390 
391 my $sarah = $fb->query->find('sarahbownds')->request->as_hashref;
392
393=head3 id
394
395An profile id like C<sarahbownds> or an object id like C<16665510298> for the Perl page.
396
397
398=head2 picture ( id )
399
400Returns a L<Facebook::Graph::Picture> object, which can be used to generate the URLs of the pictures of any object on Facebook.
401
402=head3 id
403
404An profile id like C<sarahbownds> or an object id like C<16665510298> for the Perl page.
405
406
407
408=head2 add_post ( [ id ] )
409
410Creates a L<Facebook::Graph::Publish::Post> object, which can be used to publish data to a user's feed/wall.
411
412=head3 id
413
414Optionally provide an object id to place it on. Requires that you have administrative access to that page/object.
415
416
417=head2 add_checkin ( [ id ] )
418
419Creates a L<Facebook::Graph::Publish::Checkin> object, which can be used to publish a checkin to a location.
420
421=head3 id
422
423Optionally provide an user id to check in. Requires that you have administrative access to that user.
424
425
426=head2 add_like ( id )
427
428Creates a L<Facebook::Graph::Publish::Like> object to tell everyone about a post you like.
429
430=head3 id
431
432The id of a post you like.
433
434
435=head2 add_comment ( id )
436
437Creates a L<Facebook::Graph::Publish::Comment> object that you can use to comment on a note.
438
439=head3 id
440
441The id of the post you want to comment on.
442
443
444=head2 add_note ( )
445
446Creates a L<Facebook::Graph::Publish::Note> object, which can be used to publish notes.
447
448
449=head2 add_link ( )
450
451Creates a L<Facebook::Graph::Publish::Link> object, which can be used to publish links.
452
453
454=head2 add_event ( [id] )
455
456Creates a L<Facebook::Graph::Publish::Event> object, which can be used to publish events.
457
458=head3 id
459
460Optionally provide an object id to place it on. Requires that you have administrative access to that page/object.
461
462
463
464=head2 rsvp_maybe ( id )
465
466RSVP as 'maybe' to an event.
467
468=head3 id
469
470The id of an event.
471
472=head2 rsvp_attending ( id )
473
474RSVP as 'attending' to an event.
475
476=head3 id
477
478The id of an event.
479
480=head2 rsvp_declined ( id )
481
482RSVP as 'declined' to an event.
483
484=head3 id
485
486The id of an event.
487
488
489
490=head2 convert_sessions ( sessions )
491
492A utility method to convert old sessions into access tokens that can be used with the Graph API. Returns an array reference of hash references of access tokens.
493
494 [
495   {
496     "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
497     "expires": 1271649600,
498   },
499   ...
500 ]
501
502See also L<Facebook::Graph::Session>.
503
504=head3 sessions
505
506An array reference of session ids from the old Facebook API.
507
508
509=head2 parse_signed_request ( signed_request )
510
511Allows the decoding of signed requests for canvas applications to ensure data passed back from Facebook isn't tampered with. You can read more about this at L<http://developers.facebook.com/docs/authentication/canvas>.
512
513=head3 signed_request
514
515A signature string passed from Facebook. To capture a signed request your app must be displayed within the Facebook canvas page and then you must pull the query parameter called C<signed_request> from the query string.
516
517B<NOTE:> To get this passed to your app you must enable it in your migration settings for your app (L<http://www.facebook.com/developers/>).
518
519=head1 EXCEPTIONS
520
521This module throws exceptions when it encounters a problem. It uses L<Ouch> to throw the exception, and the Exception typically takes 3 parts: code, message, and a data portion that is the URI that was originally requested. For example:
522
523 eval { $fb->call_some_method };
524 if (kiss 500) {
525   say "error: ". $@->message;
526   say "uri: ".$@->data;
527 }
528 else {
529   throw $@; # rethrow the error
530 }
531
532
533=head1 TODO
534
535I still need to add publishing albums/photos, deleting of content, impersonation, and analytics to have a feature complete API. In addition, the module could use a lot more tests.
536
537
538=head1 PREREQS
539
540L<Any::Moose>
541L<JSON>
542L<LWP>
543L<LWP::Protocol::https>
544L<Mozilla::CA>
545L<URI>
546L<DateTime>
547L<DateTime::Format::Strptime>
548L<MIME::Base64::URLSafe>
549L<URI::Encode>
550L<Ouch>
551
552=head2 Optional
553
554L<Digest::SHA> is used for signed requests. If you don't plan on using the signed request feature, then you do not need to install Digest::SHA.
555
556=head1 SUPPORT
557
558=over
559
560=item Repository
561
562L<http://github.com/rizen/Facebook-Graph>
563
564=item Bug Reports
565
566L<http://github.com/rizen/Facebook-Graph/issues>
567
568=back
569
570
571=head1 SEE ALSO
572
573If you're looking for a fully featured Facebook client in Perl I highly recommend L<WWW::Facebook::API>. It does just about everything, it just uses the old Facebook API.
574
575=head1 AUTHOR
576
577JT Smith <jt_at_plainblack_dot_com>
578
579=head1 LEGAL
580
581Facebook::Graph is Copyright 2010 Plain Black Corporation (L<http://www.plainblack.com>) and is licensed under the same terms as Perl itself.
582
583=cut
Note: See TracBrowser for help on using the repository browser.