From 5a4ca9459ddf60d4b19748625b73015ff6149a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B6=C3=9Fl?= Date: Wed, 29 Feb 2012 14:06:19 +0000 Subject: [PATCH] Real-Time Updates --- facebook/README | 5 +- facebook/facebook.php | 348 +++++++++++++++++++++++++----------------- 2 files changed, 210 insertions(+), 143 deletions(-) diff --git a/facebook/README b/facebook/README index ee7e489b..e2a3c838 100755 --- a/facebook/README +++ b/facebook/README @@ -23,7 +23,10 @@ Installing the Friendica/Facebook connector and click 'Install Facebook Connector'. 4. This will ask you to login to Facebook and grant permission to the plugin to do its stuff. Allow it to do so. -5. You're done. To turn it off visit the Plugin Settings page again and +5. Optional step: If you want to use Facebook Real Time Updates (so new messages + and new contacts are added ~1min after they are postet / added on FB), go to + Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button. +6. You're done. To turn it off visit the Plugin Settings page again and 'Remove Facebook posting'. Vidoes and embeds will not be posted if there is no other content. Links diff --git a/facebook/facebook.php b/facebook/facebook.php index 039b27ff..0e30b62b 100755 --- a/facebook/facebook.php +++ b/facebook/facebook.php @@ -1,8 +1,9 @@ + * Tobias Hößl */ /** @@ -31,7 +32,10 @@ * and click 'Install Facebook Connector'. * 4. This will ask you to login to Facebook and grant permission to the * plugin to do its stuff. Allow it to do so. - * 5. You're done. To turn it off visit the Plugin Settings page again and + * 5. Optional step: If you want to use Facebook Real Time Updates (so new messages + * and new contacts are added ~1min after they are postet / added on FB), go to + * Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button. + * 6. You're done. To turn it off visit the Plugin Settings page again and * 'Remove Facebook posting'. * * Vidoes and embeds will not be posted if there is no other content. Links @@ -53,6 +57,8 @@ function facebook_install() { register_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings'); register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron'); register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook'); + + if (get_config('facebook', 'realtime_active') == 1) facebook_subscription_add_users(); // Restore settings, if the plugin was installed before } @@ -67,6 +73,8 @@ function facebook_uninstall() { // hook moved unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook'); unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings'); + + if (get_config('facebook', 'realtime_active') == 1) facebook_subscription_del_users(); } @@ -113,7 +121,7 @@ function facebook_init(&$a) { return; } - $affected_users = array("feed" => array(), "friends" => array(), "activities" => array()); + $affected_users = array("feed" => array(), "friends" => array()); foreach ($js->entry as $entry) { $fbuser = $entry->uid; @@ -123,19 +131,20 @@ function facebook_init(&$a) { continue; } if (in_array($fbuser, $affected_users[$field])) continue; + + $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser)); + if(! count($r)) + continue; + $uid = $r[0]['uid']; + + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token) + return; + switch ($field) { case "feed": logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG); - $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser)); - if(! count($r)) - continue; - $uid = $r[0]['uid']; - - $access_token = get_pconfig($uid,'facebook','access_token'); - if(! $access_token) - return; - if(! get_pconfig($uid,'facebook','no_wall')) { $private_wall = intval(get_pconfig($uid,'facebook','private_wall')); $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token); @@ -147,12 +156,14 @@ function facebook_init(&$a) { } break; - case "friend": - // @TODO - break; - case "activities": - //@TODO + case "friends": + logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG); + + fb_get_friends($uid, false); + set_pconfig($uid,'facebook','friend_check',time()); break; + default: + logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL); } $affected_users[$field][] = $fbuser; } @@ -171,8 +182,8 @@ function facebook_init(&$a) { return; $uid = $r[0]['uid']; - $auth_code = (($_GET['code']) ? $_GET['code'] : ''); - $error = (($_GET['error_description']) ? $_GET['error_description'] : ''); + $auth_code = (x($_GET, 'code') ? $_GET['code'] : ''); + $error = (x($_GET, 'error_description') ? $_GET['error_description'] : ''); if($error) @@ -199,7 +210,7 @@ function facebook_init(&$a) { if(get_pconfig($uid,'facebook','no_linking') === false) set_pconfig($uid,'facebook','no_linking',1); fb_get_self($uid); - fb_get_friends($uid); + fb_get_friends($uid, true); fb_consume_all($uid); } @@ -220,9 +231,130 @@ function fb_get_self($uid) { } } +function fb_get_friends_sync_new($uid, $access_token, $person) { + $link = 'http://facebook.com/profile.php?id=' . $person->id; + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", + intval($uid), + dbesc($link) + ); + + if (count($r) == 0) { + logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG); + + fb_get_friends_sync_full($uid, $access_token, $person); + } +} +function fb_get_friends_sync_full($uid, $access_token, $person) { + $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token); + if($s) { + $jp = json_decode($s); + logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA); -function fb_get_friends($uid) { + // always use numeric link for consistency + + $jp->link = 'http://facebook.com/profile.php?id=' . $person->id; + + // check if we already have a contact + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", + intval($uid), + dbesc($jp->link) + ); + + if(count($r)) { + + // check that we have all the photos, this has been known to fail on occasion + + if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) { + require_once("Photo.php"); + + $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']); + + $r = q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d LIMIT 1 + ", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($r[0]['id']) + ); + } + continue; + } + else { + + // create contact record + $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`, + `name`, `nick`, `photo`, `network`, `rel`, `priority`, + `writable`, `blocked`, `readonly`, `pending` ) + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", + intval($uid), + dbesc(datetime_convert()), + dbesc($jp->link), + dbesc(normalise_link($jp->link)), + dbesc(''), + dbesc(''), + dbesc($jp->id), + dbesc('facebook ' . $jp->id), + dbesc($jp->name), + dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)), + dbesc('https://graph.facebook.com/' . $jp->id . '/picture'), + dbesc(NETWORK_FACEBOOK), + intval(CONTACT_IS_FRIEND), + intval(1), + intval(1) + ); + } + + $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", + dbesc($jp->link), + intval($uid) + ); + + if(! count($r)) { + continue; + } + + $contact = $r[0]; + $contact_id = $r[0]['id']; + + require_once("Photo.php"); + + $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id); + + $r = q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d LIMIT 1 + ", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($contact_id) + ); + + } +} + +// if $fullsync is true, only new contacts are searched for + +function fb_get_friends($uid, $fullsync = true) { $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1", intval($uid) @@ -245,111 +377,11 @@ function fb_get_friends($uid) { logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA); if(! $j->data) return; - foreach($j->data as $person) { - $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token); - if($s) { - $jp = json_decode($s); - logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA); - - // always use numeric link for consistency - - $jp->link = 'http://facebook.com/profile.php?id=' . $person->id; - - // check if we already have a contact - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", - intval($uid), - dbesc($jp->link) - ); - - if(count($r)) { - - // check that we have all the photos, this has been known to fail on occasion - - if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) { - require_once("Photo.php"); - - $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']); - - $r = q("UPDATE `contact` SET `photo` = '%s', - `thumb` = '%s', - `micro` = '%s', - `name-date` = '%s', - `uri-date` = '%s', - `avatar-date` = '%s' - WHERE `id` = %d LIMIT 1 - ", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($r[0]['id']) - ); - } - continue; - } - else { - - // create contact record - $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`, - `name`, `nick`, `photo`, `network`, `rel`, `priority`, - `writable`, `blocked`, `readonly`, `pending` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", - intval($uid), - dbesc(datetime_convert()), - dbesc($jp->link), - dbesc(normalise_link($jp->link)), - dbesc(''), - dbesc(''), - dbesc($jp->id), - dbesc('facebook ' . $jp->id), - dbesc($jp->name), - dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)), - dbesc('https://graph.facebook.com/' . $jp->id . '/picture'), - dbesc(NETWORK_FACEBOOK), - intval(CONTACT_IS_FRIEND), - intval(1), - intval(1) - ); - } - - $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", - dbesc($jp->link), - intval($uid) - ); - - if(! count($r)) { - continue; - } - - $contact = $r[0]; - $contact_id = $r[0]['id']; - - require_once("Photo.php"); - - $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id); - - $r = q("UPDATE `contact` SET `photo` = '%s', - `thumb` = '%s', - `micro` = '%s', - `name-date` = '%s', - `uri-date` = '%s', - `avatar-date` = '%s' - WHERE `id` = %d LIMIT 1 - ", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($contact_id) - ); - - } - } + foreach($j->data as $person) + if ($fullsync) + fb_get_friends_sync_full($uid, $access_token, $person); + else + fb_get_friends_sync_new($uid, $access_token, $person); } } @@ -394,7 +426,7 @@ function facebook_post(&$a) { elseif(intval($no_linking) && intval($linkvalue)) { // FB linkage is now allowed - import stuff. fb_get_self($uid); - fb_get_friends($uid); + fb_get_friends($uid, true); fb_consume_all($uid); } @@ -419,7 +451,7 @@ function facebook_content(&$a) { } if($a->argc > 1 && $a->argv[1] === 'friends') { - fb_get_friends(local_user()); + fb_get_friends(local_user(), true); info( t('Updating contacts') . EOL); } @@ -537,13 +569,40 @@ function facebook_cron($a,$b) { if($last_friend_check) $next_friend_check = $last_friend_check + 86400; if($next_friend_check <= time()) { - fb_get_friends($rr['uid']); + fb_get_friends($rr['uid'], true); set_pconfig($rr['uid'],'facebook','friend_check',time()); } fb_consume_all($rr['uid']); } - } - + } + + if (get_config('facebook', 'realtime_active') == 1) { + if (!facebook_check_realtime_active()) { + + logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL); + facebook_subscription_add_users(); + + if (facebook_check_realtime_active()) + logger('facebook_cron: Successful', LOGGER_NORMAL); + else { + logger('facebook_cron: Failed', LOGGER_NORMAL); + + if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent')) { + $res = mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'), + "Hi!\n\nThere's a problem with the Facebook Real-Time Updates that cannob be solved automatically. Maybe an permission issue?\n\nThis e-mail will only be sent once.", + 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n" + . 'Content-type: text/plain; charset=UTF-8' . "\n" + . 'Content-transfer-encoding: 8bit' + ); + + set_config('facebook', 'realtime_err_mailsent', 1); + } + } + } else { // !facebook_check_realtime_active() + del_config('facebook', 'realtime_err_mailsent'); + } + } + set_config('facebook','last_poll', time()); } @@ -562,12 +621,7 @@ function facebook_plugin_settings(&$a,&$b) { function facebook_plugin_admin(&$a, &$o){ - $activated = false; - $access_token = fb_get_app_access_token(); - if ($access_token) { - $ret = facebook_subscriptions_get(); - if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") $activated = true; - } + $activated = facebook_check_realtime_active(); if ($activated) { $o = t('Real-Time Updates are activated.') . '

'; $o .= ''; @@ -1300,7 +1354,9 @@ function facebook_subscription_del_users() { $access_token = fb_get_app_access_token(); $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token; - delete_url($url); + facebook_delete_url($url); + + del_config('facebook', 'realtime_active'); } function facebook_subscription_add_users() { @@ -1318,7 +1374,7 @@ function facebook_subscription_add_users() { $j = post_url($url,array( "object" => "user", - "fields" => "feed,friends,activities", + "fields" => "feed,friends", "callback_url" => $cb, "verify_token" => $verify_token, )); @@ -1326,12 +1382,15 @@ function facebook_subscription_add_users() { if ($j) { logger("Facebook reponse: " . $j, LOGGER_DATA); + + if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1); }; } function facebook_subscriptions_get() { $access_token = fb_get_app_access_token(); + if (!$access_token) return null; $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token; $j = fetch_url($url); @@ -1344,15 +1403,20 @@ function facebook_subscriptions_get() { } - +function facebook_check_realtime_active() { + $ret = facebook_subscriptions_get(); + if (is_null($ret)) return false; + if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true; + return false; +} // DELETE-request to $url -if(! function_exists('delete_url')) { -function delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) { +if(! function_exists('facebook_delete_url')) { +function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) { $a = get_app(); $ch = curl_init($url); if(($redirects > 8) || (! $ch))