- Timestamp:
- Sep 20, 2011, 11:15:32 PM (13 years ago)
- Branches:
- master, release-1.10, release-1.9
- Children:
- b7fa912
- Parents:
- 2a42248
- git-author:
- Edward Z. Yang <ezyang@mit.edu> (07/12/11 09:17:04)
- git-committer:
- Edward Z. Yang <ezyang@mit.edu> (09/20/11 23:15:32)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
perl/modules/Facebook/lib/BarnOwl/Module/Facebook/Handle.pm
rd9c6631 r7777ac2 165 165 sub check_result { 166 166 my $self = shift; 167 if (kiss 400) { 168 # Ugh, no easy way of accessing the JSON error type 169 # which is OAuthException. 167 if (kiss "OAuthException") { 170 168 $self->{logged_in} = 0; 171 169 $self->facebook_do_auth; … … 185 183 return unless $self->{logged_in}; 186 184 187 my $friends = eval { $self->{facebook}->fetch('me/friends'); }; 188 return unless $self->check_result; 189 190 $self->{friends} = {}; 191 192 for my $friend (@{$friends->{data}}) { 193 if (defined $self->{friends}{$friend->{name}}) { 194 # XXX We should try a little harder here, rather than just 195 # tacking on a number. Ideally, we should be able to 196 # calculate some extra piece of information that the user 197 # needs to disambiguate between the two users. An old 198 # version of Facebook used to disambiguate with your primary 199 # network (so you might have Edward Yang (MIT) and Edward 200 # Yang (Cambridge), the idea being that users in the same 201 # network would probably have already disambiguated 202 # themselves with middle names or nicknames. We no longer 203 # get network information, since Facebook axed that 204 # information, but the Education/Work fields may still be 205 # a reasonable approximation (but which one do you pick?! 206 # The most recent one.) Since getting this information 207 # involves extra queries, there are also caching and 208 # efficiency concerns (though hopefully you don't have too 209 # many friends with the same name). Furthermore, accessing 210 # this information requires a pretty hefty extra set of 211 # permissions requests, which we don't currently ask for. 212 # It may just be better to let users specify custom 213 # aliases for Facebook users, which are added into this 214 # hash. See also username support. 215 warn "Duplicate friend name " . $friend->{name}; 216 my $name = $friend->{name}; 217 my $i = 2; 218 while (defined $self->{friends}{$friend->{name} . ' ' . $i}) { $i++; } 219 $self->{friends}{$friend->{name} . ' ' . $i} = $friend->{id}; 220 } else { 221 $self->{friends}{$friend->{name}} = $friend->{id}; 185 $self->{facebook}->query->find('me/friends')->request(sub { 186 my $response = shift; 187 my $friends = eval { $response->as_hashref }; 188 return unless $self->check_result; 189 190 $self->{friends} = {}; 191 192 for my $friend (@{$friends->{data}}) { 193 if (defined $self->{friends}{$friend->{name}}) { 194 # XXX We should try a little harder here, rather than just 195 # tacking on a number. Ideally, we should be able to 196 # calculate some extra piece of information that the user 197 # needs to disambiguate between the two users. An old 198 # version of Facebook used to disambiguate with your primary 199 # network (so you might have Edward Yang (MIT) and Edward 200 # Yang (Cambridge), the idea being that users in the same 201 # network would probably have already disambiguated 202 # themselves with middle names or nicknames. We no longer 203 # get network information, since Facebook axed that 204 # information, but the Education/Work fields may still be 205 # a reasonable approximation (but which one do you pick?! 206 # The most recent one.) Since getting this information 207 # involves extra queries, there are also caching and 208 # efficiency concerns (though hopefully you don't have too 209 # many friends with the same name). Furthermore, accessing 210 # this information requires a pretty hefty extra set of 211 # permissions requests, which we don't currently ask for. 212 # It may just be better to let users specify custom 213 # aliases for Facebook users, which are added into this 214 # hash. See also username support. 215 warn "Duplicate friend name " . $friend->{name}; 216 my $name = $friend->{name}; 217 my $i = 2; 218 while (defined $self->{friends}{$friend->{name} . ' ' . $i}) { $i++; } 219 $self->{friends}{$friend->{name} . ' ' . $i} = $friend->{id}; 220 } else { 221 $self->{friends}{$friend->{name}} = $friend->{id}; 222 } 222 223 } 223 } 224 225 # XXX We should also have support for usernames, and not just real226 # names. However, since this data is not returned by the friends227 # query, it would require a rather expensive set of queries. We228 # might try to preserve old data, but all-in-all it's a bit229 # complicated. One possible way of fixing this is to construct a230 # custom FQL query that joins the friends table and the users table.224 225 # XXX We should also have support for usernames, and not just real 226 # names. However, since this data is not returned by the friends 227 # query, it would require a rather expensive set of queries. We 228 # might try to preserve old data, but all-in-all it's a bit 229 # complicated. One possible way of fixing this is to construct a 230 # custom FQL query that joins the friends table and the users table. 231 }); 231 232 } 232 233 … … 244 245 $self->{topics} = {}; 245 246 246 my $updates = eval { 247 $self->{facebook} 248 ->query 249 ->from("my_news") 250 # Not using this, because we want to pick up comment 251 # updates. We need to manually de-duplicate, though. 252 # ->where_since("@" . $self->{last_poll}) 253 # Facebook doesn't actually give us that many results. 254 # But it can't hurt to ask! 255 ->limit_results(200) 256 ->request 257 ->as_hashref 258 }; 259 return unless $self->check_result; 260 261 my $new_last_poll = $self->{last_poll}; 262 for my $post (reverse @{$updates->{data}}) { 263 # No app invites, thanks! (XXX make configurable) 264 if ($post->{type} eq 'link' && $post->{application}) { 265 next; 266 } 267 268 # XXX Filtering out interest groups for now 269 # A more reasonable strategy may be to show their 270 # posts, but not the comments. 271 if (defined $post->{from}{category}) { 272 next; 273 } 274 275 # There can be multiple recipients! Strange! Pick the first one. 276 my $name = $post->{to}{data}[0]{name} || $post->{from}{name}; 277 my $name_id = $post->{to}{data}[0]{id} || $post->{from}{id}; 278 my $post_id = $post->{id}; 279 280 my $topic; 281 if (defined $old_topics->{$post_id}) { 282 $topic = $old_topics->{$post_id}; 283 $self->{topics}->{$post_id} = $topic; 284 } else { 285 my @keywords = keywords($post->{name} || $post->{message}); 286 $topic = $keywords[0] || 'personal'; 287 $topic =~ s/ /-/g; 288 $self->{topics}->{$post_id} = $topic; 289 } 290 291 # Only handle post if it's new 292 my $created_time = str2time($post->{created_time}); 293 if ($created_time >= $self->{last_poll}) { 294 # XXX indexing is fragile 295 my $msg = BarnOwl::Message->new( 296 type => 'Facebook', 297 sender => $post->{from}{name}, 298 sender_id => $post->{from}{id}, 299 name => $name, 300 name_id => $name_id, 301 direction => 'in', 302 body => $self->format_body($post), 303 post_id => $post_id, 304 topic => $topic, 305 time => asctime(localtime $created_time), 306 # XXX The intent is to get the 'Comment' link, which also 307 # serves as a canonical link to the post. The {name} 308 # field should equal 'Comment'. 309 permalink => $post->{actions}[0]{link}, 310 ); 311 BarnOwl::queue_message($msg); 312 } 313 314 # This will interleave times (they'll all be organized by parent 315 # post), but since we don't expect too many updates between 316 # polls this is pretty acceptable. 317 my $updated_time = str2time($post->{updated_time}); 318 if ($updated_time >= $self->{last_poll} && defined $post->{comments}{data}) { 319 for my $comment (@{$post->{comments}{data}}) { 320 my $comment_time = str2time($comment->{created_time}); 321 if ($comment_time < $self->{last_poll}) { 322 next; 323 } 247 $self->{facebook} 248 ->query 249 ->from("my_news") 250 # Not using this, because we want to pick up comment 251 # updates. We need to manually de-duplicate, though. 252 # ->where_since("@" . $self->{last_poll}) 253 # Facebook doesn't actually give us that many results. 254 # But it can't hurt to ask! 255 ->limit_results(200) 256 ->request(sub { 257 258 my $updates = eval { shift->as_hashref }; 259 return unless $self->check_result; 260 261 my $new_last_poll = $self->{last_poll}; 262 for my $post (reverse @{$updates->{data}}) { 263 # No app invites, thanks! (XXX make configurable) 264 if ($post->{type} eq 'link' && $post->{application}) { 265 next; 266 } 267 268 # XXX Filtering out interest groups for now 269 # A more reasonable strategy may be to show their 270 # posts, but not the comments. 271 if (defined $post->{from}{category}) { 272 next; 273 } 274 275 # There can be multiple recipients! Strange! Pick the first one. 276 my $name = $post->{to}{data}[0]{name} || $post->{from}{name}; 277 my $name_id = $post->{to}{data}[0]{id} || $post->{from}{id}; 278 my $post_id = $post->{id}; 279 280 my $topic; 281 if (defined $old_topics->{$post_id}) { 282 $topic = $old_topics->{$post_id}; 283 $self->{topics}->{$post_id} = $topic; 284 } else { 285 my @keywords = keywords($post->{name} || $post->{message}); 286 $topic = $keywords[0] || 'personal'; 287 $topic =~ s/ /-/g; 288 $self->{topics}->{$post_id} = $topic; 289 } 290 291 # Only handle post if it's new 292 my $created_time = str2time($post->{created_time}); 293 if ($created_time >= $self->{last_poll}) { 294 # XXX indexing is fragile 324 295 my $msg = BarnOwl::Message->new( 325 296 type => 'Facebook', 326 sender => $ comment->{from}{name},327 sender_id => $ comment->{from}{id},297 sender => $post->{from}{name}, 298 sender_id => $post->{from}{id}, 328 299 name => $name, 329 300 name_id => $name_id, 330 301 direction => 'in', 331 body => $ comment->{message},302 body => $self->format_body($post), 332 303 post_id => $post_id, 333 304 topic => $topic, 334 time => asctime(localtime $comment_time), 305 time => asctime(localtime $created_time), 306 # XXX The intent is to get the 'Comment' link, which also 307 # serves as a canonical link to the post. The {name} 308 # field should equal 'Comment'. 309 permalink => $post->{actions}[0]{link}, 335 310 ); 336 311 BarnOwl::queue_message($msg); 337 312 } 313 314 # This will interleave times (they'll all be organized by parent 315 # post), but since we don't expect too many updates between 316 # polls this is pretty acceptable. 317 my $updated_time = str2time($post->{updated_time}); 318 if ($updated_time >= $self->{last_poll} && defined $post->{comments}{data}) { 319 for my $comment (@{$post->{comments}{data}}) { 320 my $comment_time = str2time($comment->{created_time}); 321 if ($comment_time < $self->{last_poll}) { 322 next; 323 } 324 my $msg = BarnOwl::Message->new( 325 type => 'Facebook', 326 sender => $comment->{from}{name}, 327 sender_id => $comment->{from}{id}, 328 name => $name, 329 name_id => $name_id, 330 direction => 'in', 331 body => $comment->{message}, 332 post_id => $post_id, 333 topic => $topic, 334 time => asctime(localtime $comment_time), 335 permalink => "", 336 ); 337 BarnOwl::queue_message($msg); 338 } 339 } 340 if ($updated_time + 1 > $new_last_poll) { 341 $new_last_poll = $updated_time + 1; 342 } 338 343 } 339 if ($updated_time + 1 > $new_last_poll) { 340 $new_last_poll = $updated_time + 1; 341 } 342 } 343 # old_topics gets GC'd 344 345 $self->{last_poll} = $new_last_poll; 344 # old_topics gets GC'd 345 346 $self->{last_poll} = $new_last_poll; 347 }); 346 348 } 347 349 … … 375 377 my $msg = shift; 376 378 379 my $cont = sub { $self->sleep(0); }; 380 377 381 if (defined $user) { 378 382 $user = $self->{friends}{$user} || $user; 379 eval { $self->{facebook}->add_post($user)->set_message($msg)->publish; }; 380 return unless $self->check_result; 383 $self->{facebook}->add_post($user)->set_message($msg)->publish($cont); 381 384 } else { 382 eval { $self->{facebook}->add_post->set_message($msg)->publish; }; 383 return unless $self->check_result; 384 } 385 $self->sleep(0); 385 $self->{facebook}->add_post->set_message($msg)->publish($cont); 386 } 386 387 } 387 388 … … 392 393 my $msg = shift; 393 394 394 eval { $self->{facebook}->add_comment($post_id)->set_message($msg)->publish; }; 395 return unless $self->check_result; 396 $self->sleep(0); 395 $self->{facebook}->add_comment($post_id)->set_message($msg)->publish(sub { $self->sleep(0); }); 397 396 } 398 397 … … 416 415 417 416 $self->{cfg}->{token} = $1; 418 if ($self->facebook_do_auth){417 $self->facebook_do_auth(sub { 419 418 my $raw_cfg = to_json($self->{cfg}); 420 419 BarnOwl::admin_message('Facebook', "Add this as the contents of your ~/.owl/facebook file:\n$raw_cfg"); 421 } 420 }); 421 return; 422 422 } 423 423 424 424 sub facebook_do_auth { 425 425 my $self = shift; 426 my $success = shift || sub {}; 426 427 if (!defined $self->{cfg}->{token}) { 427 428 BarnOwl::admin_message('Facebook', "Login to Facebook at ".$self->{login_url} … … 437 438 $self->{facebook}->access_token($self->{cfg}->{token}); 438 439 # Do a quick check to see if things are working 439 my $result = eval { $self->{facebook}->query()->find('me')->select_fields('name')->request->as_hashref; }; 440 if ($@) { 441 BarnOwl::admin_message('Facebook', "Failed to authenticate with '$@'!" 442 . "\nLogin to Facebook at ".$self->{login_url} 443 . "\nand run command ':facebook-auth URL' with the URL you are redirected to."); 444 return 0; 445 } else { 446 my $name = $result->{'name'}; 447 BarnOwl::admin_message('Facebook', "Successfully logged in to Facebook as $name!"); 448 $self->{logged_in} = 1; 449 $self->sleep(0); # start polling 450 return 1; 451 } 440 $self->{facebook}->query()->find('me')->select_fields('name')->request(sub { 441 my $result = eval { shift->as_hashref }; 442 if ($@) { 443 BarnOwl::admin_message('Facebook', "Failed to authenticate with '$@'!" 444 . "\nLogin to Facebook at ".$self->{login_url} 445 . "\nand run command ':facebook-auth URL' with the URL you are redirected to."); 446 } else { 447 my $name = $result->{'name'}; 448 BarnOwl::admin_message('Facebook', "Successfully logged in to Facebook as $name!"); 449 $self->{logged_in} = 1; 450 $self->sleep(0); # start polling 451 $success->(); 452 } 453 }); 452 454 } 453 455
Note: See TracChangeset
for help on using the changeset viewer.