Changeset bc56a0d for perl/modules/Facebook/lib
- Timestamp:
- Sep 19, 2011, 1:31:34 PM (13 years ago)
- Children:
- ac45403
- Parents:
- a8e1fcf
- git-author:
- Edward Z. Yang <ezyang@mit.edu> (06/22/11 21:53:17)
- git-committer:
- Edward Z. Yang <ezyang@mit.edu> (09/19/11 13:31:34)
- Location:
- perl/modules/Facebook/lib/BarnOwl/Module
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
perl/modules/Facebook/lib/BarnOwl/Module/Facebook.pm
rc833c3d rbc56a0d 24 24 our $facebook_handle = undef; 25 25 26 # did not implement class monitoring27 # did not implement multiple accounts28 29 26 BarnOwl::new_variable_bool( 30 27 'facebook:poll', … … 32 29 default => 1, 33 30 summary => 'Poll Facebook for wall updates', 34 # TODO: Make thisconfigurable31 # XXX: Make poll time configurable 35 32 description => "If set, will poll Facebook every minute for updates.\n" 36 33 } 37 34 ); 38 35 39 sub fail { 40 my $msg = shift; 41 # reset global state here 42 BarnOwl::admin_message('Facebook Error', $msg); 43 die("Facebook Error: $msg\n"); 36 sub init { 37 my $conffile = BarnOwl::get_config_dir() . "/facebook"; 38 my $cfg = {}; 39 if (open(my $fh, "<", "$conffile")) { 40 my $raw_cfg = do {local $/; <$fh>}; 41 close($fh); 42 43 eval { $cfg = from_json($raw_cfg); }; 44 if ($@) { BarnOwl::admin_message('Facebook', "Unable to parse $conffile: $@"); } 45 } 46 eval { $facebook_handle = BarnOwl::Module::Facebook::Handle->new($cfg); }; 47 if ($@) { BarnOwl::error($@); } 44 48 } 45 49 46 # We only load up when the conf file is present, to reduce resource 47 # usage. Though, probably not by very much, so maybe a 'facebook-init' 48 # command would be more appropriate. 49 50 my $conffile = BarnOwl::get_config_dir() . "/facebook"; 51 52 if (open(my $fh, "<", "$conffile")) { 53 read_config($fh); 54 close($fh); 55 } 56 57 sub read_config { 58 my $fh = shift; 59 my $raw_cfg = do {local $/; <$fh>}; 60 close($fh); 61 62 my $cfg; 63 if ($raw_cfg) { 64 eval { $cfg = from_json($raw_cfg); }; 65 if($@) { 66 fail("Unable to parse $conffile: $@"); 67 } 68 } else { 69 $cfg = {}; 70 } 71 72 eval { 73 $facebook_handle = BarnOwl::Module::Facebook::Handle->new($cfg); 74 }; 75 if ($@) { 76 BarnOwl::error($@); 77 next; 78 } 79 } 80 81 # Ostensibly here as a convenient shortcut for Perl hackery 82 sub facebook { 83 $facebook_handle->facebook(@_); 84 } 50 init(); 85 51 86 52 # Should also add support for posting to other people's walls (this … … 89 55 BarnOwl::new_command('facebook' => \&cmd_facebook, { 90 56 summary => 'Post a status update to your wall from BarnOwl', 91 usage => 'facebook ',92 description => 'Post a status update to your wall .'57 usage => 'facebook [USER]', 58 description => 'Post a status update to your wall, or post on another user\'s wall. Autocomplete is supported.' 93 59 }); 94 60 95 # How do we allow people to specify the USER?96 61 #BarnOwl::new_command('facebook-message' => \&cmd_facebook_direct, { 97 62 # summary => 'Send a Facebook message', … … 103 68 summary => 'Comment on a wall post.', 104 69 usage => 'facebook-comment POST_ID', 105 description => 'Comment on a friend\'s wall post. Using r is recommended.'70 description => 'Comment on a friend\'s wall post.' 106 71 }); 107 72 … … 116 81 summary => 'Force a poll of Facebook.', 117 82 usage => 'facebook-poll', 118 description => 'Get updates from Facebook.'83 description => 'Get updates (news, friends) from Facebook.' 119 84 }); 120 121 # XXX: UI: probably should bug out immediately if we're not logged in.122 85 123 86 sub cmd_facebook { … … 125 88 my $user = shift; 126 89 90 return unless check_ready(); 91 127 92 BarnOwl::start_edit_win( 128 93 defined $user ? "Write something to $user..." : "What's on your mind?", 129 sub{ facebook($user, shift) }94 sub{ $facebook_handle->facebook($user, shift) } 130 95 ); 131 96 } … … 135 100 my $post_id = shift; 136 101 102 return unless check_ready(); 103 137 104 my $topic = $facebook_handle->get_topic($post_id); 138 105 139 # XXX UI should give some (better) indication /which/ conversation140 # is being commented on141 106 BarnOwl::start_edit_win("Write a comment on '$topic'...", 142 107 sub { $facebook_handle->facebook_comment($post_id, shift) }); … … 145 110 sub cmd_facebook_poll { 146 111 my $cmd = shift; 112 113 return unless check_ready(); 147 114 148 115 $facebook_handle->sleep(0); … … 154 121 my $url = shift; 155 122 123 if ($facebook_handle->{logged_in}) { 124 BarnOwl::message("Already logged in. (To force, run ':reload-module Facebook', or deauthorize BarnOwl.)"); 125 return; 126 } 127 156 128 $facebook_handle->facebook_auth($url); 157 129 } 158 130 131 sub check_ready { 132 if (!$facebook_handle->{logged_in}) { 133 BarnOwl::message("Need to login to Facebook first with ':facebook-auth'."); 134 return 0; 135 } 136 return 1; 137 } 138 159 139 BarnOwl::filter(qw{facebook type ^facebook$}); 160 161 # Autocompletion support162 140 163 141 sub complete_user { return keys %{$facebook_handle->{friends}}; } -
perl/modules/Facebook/lib/BarnOwl/Module/Facebook/Handle.pm
ra8e1fcf rbc56a0d 35 35 use Date::Parse; 36 36 use POSIX; 37 use Ouch; 37 38 38 39 use Scalar::Util qw(weaken); … … 65 66 # comments less frequently than polling for new posts. 66 67 67 sub fail {68 my $self = shift;69 my $msg = shift;70 undef $self->{facebook};71 die("[Facebook] Error: $msg\n");72 }73 74 68 sub new { 75 69 my $class = shift; … … 83 77 # but we can't assume that the BarnOwl lives on a publically 84 78 # addressable server (XXX maybe we can setup an option for this.) 85 'last_friend_poll' => 0,86 79 'friend_timer' => undef, 87 80 88 81 # Initialized with our 'time', but will be synced to Facebook 89 82 # soon enough. (Subtractive amount is just to preseed with some 90 # values.) 91 'last_poll' => time - 60 * 60 * 24 * 2,83 # values.) XXX Remove subtraction altogether. 84 'last_poll' => time - 60 * 60, 92 85 'timer' => undef, 93 86 … … 99 92 # $fb->authorize, but at time of writing (1.0300) they didn't support 100 93 # the response_type parameter. 101 # 'login_url' => 'https://www.facebook.com/dialog/oauth?client_id=235537266461636&scope=read_stream,read_mailbox,publish_stream,offline_access &redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token',94 # 'login_url' => 'https://www.facebook.com/dialog/oauth?client_id=235537266461636&scope=read_stream,read_mailbox,publish_stream,offline_access,read_friendlists,rsvp_event,user_events&redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token', 102 95 # minified to fit in most terminal windows. 103 'login_url' => 'http://goo.gl/yA42G', 96 # Be careful about updating these values, since BarnOwl will not 97 # notice that it is missing necessary permissions until it 98 # attempt to perform an operation which fails due to lack of 99 # permissions. 100 'login_url' => 'http://goo.gl/rcM9s', 104 101 105 102 'logged_in' => 0, … … 164 161 } 165 162 163 sub check_result { 164 my $self = shift; 165 if (kiss 400) { 166 # Ugh, no easy way of accessing the JSON error type 167 # which is OAuthException. 168 $self->{logged_in} = 0; 169 $self->facebook_do_auth; 170 return 0; 171 } elsif (hug) { 172 my $code = $@->code; 173 warn "Poll failed with $code: $@"; 174 return 0; 175 } 176 return 1; 177 } 178 166 179 sub poll_friends { 167 180 my $self = shift; … … 171 184 172 185 my $friends = eval { $self->{facebook}->fetch('me/friends'); }; 173 if ($@) { 174 warn "Poll failed $@"; 175 return; 176 } 177 178 $self->{last_friend_poll} = time; 186 return unless $self->check_result; 187 179 188 $self->{friends} = {}; 180 189 … … 195 204 # The most recent one.) Since getting this information 196 205 # involves extra queries, there are also caching and 197 # efficiency concerns. 198 # We may want a facility for users to specify custom 206 # efficiency concerns (though hopefully you don't have too 207 # many friends with the same name). Furthermore, accessing 208 # this information requires a pretty hefty extra set of 209 # permissions requests, which we don't currently ask for. 210 # It may just be better to let users specify custom 199 211 # aliases for Facebook users, which are added into this 200 212 # hash. See also username support. … … 213 225 # query, it would require a rather expensive set of queries. We 214 226 # might try to preserve old data, but all-in-all it's a bit 215 # complicated, so we don't bother. 227 # complicated. One possible way of fixing this is to construct a 228 # custom FQL query that joins the friends table and the users table. 216 229 } 217 230 … … 219 232 my $self = shift; 220 233 221 #return unless ( time - $self->{last_poll} ) >= 60;222 234 return unless BarnOwl::getvar('facebook:poll') eq 'on'; 223 235 return unless $self->{logged_in}; 224 225 #BarnOwl::message("Polling Facebook...");226 236 227 237 # XXX Oh no! This blocks the user interface. Not good. … … 237 247 ->from("my_news") 238 248 # Not using this, because we want to pick up comment 239 # updates. We need to manually de-dup , though.249 # updates. We need to manually de-duplicate, though. 240 250 # ->where_since( "@" . $self->{last_poll} ) 251 # Facebook doesn't actually give us that many results. 252 # But it can't hurt to ask! 241 253 ->limit_results( 200 ) 242 ->request ()243 ->as_hashref ()254 ->request 255 ->as_hashref 244 256 }; 245 if ($@) { 246 warn "Poll failed $@"; 247 return; 248 } 257 return unless $self->check_result; 249 258 250 259 my $new_last_poll = $self->{last_poll}; … … 262 271 } 263 272 264 # XXX Need to somehow access Facebook's user hiding265 # mechanism266 267 273 # There can be multiple recipients! Strange! Pick the first one. 268 274 my $name = $post->{to}{data}[0]{name} || $post->{from}{name}; … … 270 276 my $post_id = $post->{id}; 271 277 278 my $topic; 272 279 if (defined $old_topics->{$post_id}) { 273 $self->{topics}->{$post_id} = $old_topics->{$post_id}; 280 $topic = $old_topics->{$post_id}; 281 $self->{topics}->{$post_id} = $topic; 274 282 } else { 275 283 my @keywords = keywords($post->{name} || $post->{message}); 276 my$topic = $keywords[0] || 'personal';284 $topic = $keywords[0] || 'personal'; 277 285 $topic =~ s/ /-/g; 278 286 $self->{topics}->{$post_id} = $topic; … … 292 300 body => $self->format_body($post), 293 301 post_id => $post_id, 294 topic => $ self->get_topic($post_id),302 topic => $topic, 295 303 time => asctime(localtime $created_time), 296 304 # XXX The intent is to get the 'Comment' link, which also … … 302 310 } 303 311 304 # This will have funky interleaving of times (they'll all be305 # sorted linearly), but since we don't expect too many updates between312 # This will interleave times (they'll all be organized by parent 313 # post), but since we don't expect too many updates between 306 314 # polls this is pretty acceptable. 307 315 my $updated_time = str2time($post->{updated_time}); … … 320 328 direction => 'in', 321 329 body => $comment->{message}, 322 post_id 323 topic => $ self->get_topic($post_id),330 post_id => $post_id, 331 topic => $topic, 324 332 time => asctime(localtime $comment_time), 325 333 ); … … 355 363 } 356 364 365 # Invariant: we don't become logged out between entering text field 366 # and actually processing the request. XXX I don't think this actually 367 # holds, but such a case would rarely happen. 368 357 369 sub facebook { 358 370 my $self = shift; … … 361 373 my $msg = shift; 362 374 363 if (!defined $self->{facebook} || !$self->{logged_in}) {364 BarnOwl::admin_message('Facebook', 'You are not currently logged into Facebook.');365 return;366 }367 375 if (defined $user) { 368 376 $user = $self->{friends}{$user} || $user; 369 $self->{facebook}->add_post( $user )->set_message( $msg )->publish; 377 eval { $self->{facebook}->add_post( $user )->set_message( $msg )->publish; }; 378 return unless $self->check_result; 370 379 } else { 371 $self->{facebook}->add_post->set_message( $msg )->publish; 380 eval { $self->{facebook}->add_post->set_message( $msg )->publish; }; 381 return unless $self->check_result; 372 382 } 373 383 $self->sleep(0); … … 380 390 my $msg = shift; 381 391 382 $self->{facebook}->add_comment( $post_id )->set_message( $msg )->publish; 392 eval { $self->{facebook}->add_comment( $post_id )->set_message( $msg )->publish; }; 393 return unless $self->check_result; 383 394 $self->sleep(0); 384 395 } … … 388 399 389 400 my $url = shift; 401 390 402 # http://www.facebook.com/connect/login_success.html#access_token=TOKEN&expires_in=0 391 403 $url =~ /access_token=([^&]+)/; # XXX Ew regex 404 405 if (!defined $1) { 406 BarnOwl::message("Invalid URL."); 407 return; 408 } 392 409 393 410 $self->{cfg}->{token} = $1; … … 402 419 if ( ! defined $self->{cfg}->{token} ) { 403 420 BarnOwl::admin_message('Facebook', "Login to Facebook at ".$self->{login_url} 404 . "\nand run command ':facebook-auth URL' with the URL you are redirected to."); 421 . "\nand run command ':facebook-auth URL' with the URL you are redirected to." 422 . "\n\nWhat does Barnowl use these permissions for? As a desktop" 423 . "\nmessaging application, we need persistent read/write access to your" 424 . "\nnews feed and your inbox. Other permissions are for pending" 425 . "\nfeatures: we intend on adding support for event streaming, RSVP," 426 . "\nand BarnOwl filtering on friend lists." 427 ); 405 428 return 0; 406 429 } 407 430 $self->{facebook}->access_token($self->{cfg}->{token}); 408 431 # Do a quick check to see if things are working 409 my $result = eval { $self->{facebook}-> fetch('me'); };432 my $result = eval { $self->{facebook}->query()->find('me')->select_fields('name')->request->as_hashref; }; 410 433 if ($@) { 411 434 BarnOwl::admin_message('Facebook', "Failed to authenticate! Login to Facebook at ".$self->{login_url} … … 421 444 } 422 445 423 sub get_topic {424 my $self = shift;425 426 my $post_id = shift;427 428 return $self->{topics}->{$post_id} || 'personal';429 }430 431 446 1;
Note: See TracChangeset
for help on using the changeset viewer.