X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=twitter%2Ftwitter.php;h=fe66886f200733ba272044ff4b4c7abce61203cc;hb=febc835a2d90509a54a41dca07dba1d8e3ed6d07;hp=99bed6e273008237d85ca2db1ab02589f0a096ef;hpb=b303aedd668010f80457cc5b0080a39c8919a09c;p=friendica-addons.git diff --git a/twitter/twitter.php b/twitter/twitter.php index 99bed6e2..fe66886f 100644 --- a/twitter/twitter.php +++ b/twitter/twitter.php @@ -64,6 +64,7 @@ use Abraham\TwitterOAuth\TwitterOAuth; use Abraham\TwitterOAuth\TwitterOAuthException; +use Codebird\Codebird; use Friendica\App; use Friendica\Content\OEmbed; use Friendica\Content\Text\BBCode; @@ -77,17 +78,17 @@ use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\Core\Worker; use Friendica\Database\DBA; +use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\GContact; use Friendica\Model\Group; use Friendica\Model\Item; use Friendica\Model\ItemContent; -use Friendica\Model\Queue; use Friendica\Model\User; -use Friendica\Object\Image; -use Friendica\Util\Config\ConfigFileLoader; +use Friendica\Protocol\Activity; +use Friendica\Util\ConfigFileLoader; use Friendica\Util\DateTimeFormat; +use Friendica\Util\Images; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -106,12 +107,11 @@ function twitter_install() Hook::register('notifier_normal' , __FILE__, 'twitter_post_hook'); Hook::register('jot_networks' , __FILE__, 'twitter_jot_nets'); Hook::register('cron' , __FILE__, 'twitter_cron'); - Hook::register('queue_predeliver' , __FILE__, 'twitter_queue_hook'); Hook::register('follow' , __FILE__, 'twitter_follow'); Hook::register('expire' , __FILE__, 'twitter_expire'); Hook::register('prepare_body' , __FILE__, 'twitter_prepare_body'); Hook::register('check_item_notification', __FILE__, 'twitter_check_item_notification'); - Logger::log("installed twitter"); + Logger::info("installed twitter"); } function twitter_uninstall() @@ -124,7 +124,6 @@ function twitter_uninstall() Hook::unregister('notifier_normal' , __FILE__, 'twitter_post_hook'); Hook::unregister('jot_networks' , __FILE__, 'twitter_jot_nets'); Hook::unregister('cron' , __FILE__, 'twitter_cron'); - Hook::unregister('queue_predeliver' , __FILE__, 'twitter_queue_hook'); Hook::unregister('follow' , __FILE__, 'twitter_follow'); Hook::unregister('expire' , __FILE__, 'twitter_expire'); Hook::unregister('prepare_body' , __FILE__, 'twitter_prepare_body'); @@ -143,7 +142,7 @@ function twitter_load_config(App $a, ConfigFileLoader $loader) function twitter_check_item_notification(App $a, array &$notification_data) { - $own_id = PConfig::get($notification_data["uid"], 'twitter', 'own_id'); + $own_id = DI::pConfig()->get($notification_data["uid"], 'twitter', 'own_id'); $own_user = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", intval($notification_data["uid"]), @@ -157,7 +156,7 @@ function twitter_check_item_notification(App $a, array &$notification_data) function twitter_follow(App $a, array &$contact) { - Logger::log("twitter_follow: Check if contact is twitter contact. " . $contact["url"], Logger::DEBUG); + Logger::info('Check if contact is twitter contact', ['url' => $contact["url"]]); if (!strstr($contact["url"], "://twitter.com") && !strstr($contact["url"], "@twitter.com")) { return; @@ -171,8 +170,8 @@ function twitter_follow(App $a, array &$contact) $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($uid, 'twitter', 'oauthtoken'); - $osecret = PConfig::get($uid, 'twitter', 'oauthsecret'); + $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret'); // If the addon is not configured (general or for this user) quit here if (empty($ckey) || empty($csecret) || empty($otoken) || empty($osecret)) { @@ -200,13 +199,13 @@ function twitter_jot_nets(App $a, array &$jotnets_fields) return; } - if (PConfig::get(local_user(), 'twitter', 'post')) { + if (DI::pConfig()->get(local_user(), 'twitter', 'post')) { $jotnets_fields[] = [ 'type' => 'checkbox', 'field' => [ 'twitter_enable', L10n::t('Post to Twitter'), - PConfig::get(local_user(), 'twitter', 'post_by_default') + DI::pConfig()->get(local_user(), 'twitter', 'post_by_default') ] ]; } @@ -242,7 +241,7 @@ function twitter_settings_post(App $a) } else { if (isset($_POST['twitter-pin'])) { // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen - Logger::log('got a Twitter PIN'); + Logger::notice('got a Twitter PIN'); $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); // the token and secret for which the PIN was generated were hidden in the settings @@ -256,24 +255,24 @@ function twitter_settings_post(App $a) $connection = new TwitterOAuth($ckey, $csecret, $_POST['twitter-token'], $_POST['twitter-token2']); $token = $connection->oauth("oauth/access_token", ["oauth_verifier" => $_POST['twitter-pin']]); // ok, now that we have the Access Token, save them in the user config - PConfig::set(local_user(), 'twitter', 'oauthtoken', $token['oauth_token']); - PConfig::set(local_user(), 'twitter', 'oauthsecret', $token['oauth_token_secret']); - PConfig::set(local_user(), 'twitter', 'post', 1); + DI::pConfig()->set(local_user(), 'twitter', 'oauthtoken', $token['oauth_token']); + DI::pConfig()->set(local_user(), 'twitter', 'oauthsecret', $token['oauth_token_secret']); + DI::pConfig()->set(local_user(), 'twitter', 'post', 1); } catch(Exception $e) { info($e->getMessage()); } catch(TwitterOAuthException $e) { info($e->getMessage()); } // reload the Addon Settings page, if we don't do it see Bug #42 - $a->internalRedirect('settings/connectors'); + DI::baseUrl()->redirect('settings/connectors'); } else { // if no PIN is supplied in the POST variables, the user has changed the setting // to post a tweet for every new __public__ posting to the wall - PConfig::set(local_user(), 'twitter', 'post', intval($_POST['twitter-enable'])); - PConfig::set(local_user(), 'twitter', 'post_by_default', intval($_POST['twitter-default'])); - PConfig::set(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror'])); - PConfig::set(local_user(), 'twitter', 'import', intval($_POST['twitter-import'])); - PConfig::set(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user'])); + DI::pConfig()->set(local_user(), 'twitter', 'post', intval($_POST['twitter-enable'])); + DI::pConfig()->set(local_user(), 'twitter', 'post_by_default', intval($_POST['twitter-default'])); + DI::pConfig()->set(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror'])); + DI::pConfig()->set(local_user(), 'twitter', 'import', intval($_POST['twitter-import'])); + DI::pConfig()->set(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user'])); if (!intval($_POST['twitter-mirror'])) { PConfig::delete(local_user(), 'twitter', 'lastid'); @@ -289,7 +288,7 @@ function twitter_settings(App $a, &$s) if (!local_user()) { return; } - $a->page['htmlhead'] .= '' . "\r\n"; + DI::page()['htmlhead'] .= '' . "\r\n"; /* * * * 1) Check that we have global consumer key & secret * 2) If no OAuthtoken & stuff is present, generate button to get some @@ -297,14 +296,14 @@ function twitter_settings(App $a, &$s) */ $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get(local_user(), 'twitter', 'oauthtoken'); - $osecret = PConfig::get(local_user(), 'twitter', 'oauthsecret'); + $otoken = DI::pConfig()->get(local_user(), 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get(local_user(), 'twitter', 'oauthsecret'); - $enabled = intval(PConfig::get(local_user(), 'twitter', 'post')); - $defenabled = intval(PConfig::get(local_user(), 'twitter', 'post_by_default')); - $mirrorenabled = intval(PConfig::get(local_user(), 'twitter', 'mirror_posts')); - $importenabled = intval(PConfig::get(local_user(), 'twitter', 'import')); - $create_userenabled = intval(PConfig::get(local_user(), 'twitter', 'create_user')); + $enabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'post')); + $defenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'post_by_default')); + $mirrorenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'mirror_posts')); + $importenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'import')); + $create_userenabled = intval(DI::pConfig()->get(local_user(), 'twitter', 'create_user')); $css = (($enabled) ? '' : '-disabled'); @@ -355,7 +354,10 @@ function twitter_settings(App $a, &$s) $field_checkbox = Renderer::getMarkupTemplate('field_checkbox.tpl'); - $s .= '
+ if (property_exists($details, 'screen_name') && + property_exists($details, 'description') && + property_exists($details, 'profile_image_url')) { + $s .= '

' . L10n::t('Currently connected to: ') . '' . $details->screen_name . '

@@ -364,6 +366,12 @@ function twitter_settings(App $a, &$s) ' . $details->description . '

'; + } else { + $s .= '
+

Invalid Twitter info

+
'; + Logger::info('Invalid twitter info (verify credentials).', ['auth' => TwitterOAuth::class]); + } $s .= '
'; $s .= Renderer::replaceMacros($field_checkbox, [ @@ -419,10 +427,10 @@ function twitter_hook_fork(App $a, array &$b) return; } - if (PConfig::get($post['uid'], 'twitter', 'import')) { + if (DI::pConfig()->get($post['uid'], 'twitter', 'import')) { // Don't fork if it isn't a reply to a twitter post if (($post['parent'] != $post['id']) && !Item::exists(['id' => $post['parent'], 'network' => Protocol::TWITTER])) { - Logger::log('No twitter parent found for item ' . $post['id']); + Logger::notice('No twitter parent found', ['item' => $post['id']]); $b['execute'] = false; return; } @@ -445,11 +453,11 @@ function twitter_post_local(App $a, array &$b) return; } - $twitter_post = intval(PConfig::get(local_user(), 'twitter', 'post')); + $twitter_post = intval(DI::pConfig()->get(local_user(), 'twitter', 'post')); $twitter_enable = (($twitter_post && !empty($_REQUEST['twitter_enable'])) ? intval($_REQUEST['twitter_enable']) : 0); // if API is used, default to the chosen settings - if ($b['api_source'] && intval(PConfig::get(local_user(), 'twitter', 'post_by_default'))) { + if ($b['api_source'] && intval(DI::pConfig()->get(local_user(), 'twitter', 'post_by_default'))) { $twitter_enable = 1; } @@ -468,8 +476,8 @@ function twitter_action(App $a, $uid, $pid, $action) { $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($uid, 'twitter', 'oauthtoken'); - $osecret = PConfig::get($uid, 'twitter', 'oauthsecret'); + $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret'); $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret); @@ -498,7 +506,7 @@ function twitter_action(App $a, $uid, $pid, $action) function twitter_post_hook(App $a, array &$b) { // Post to Twitter - if (!PConfig::get($b["uid"], 'twitter', 'import') + if (!DI::pConfig()->get($b["uid"], 'twitter', 'import') && ($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))) { return; } @@ -550,11 +558,11 @@ function twitter_post_hook(App $a, array &$b) } } - if (($b['verb'] == ACTIVITY_POST) && $b['deleted']) { + if (($b['verb'] == Activity::POST) && $b['deleted']) { twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete"); } - if ($b['verb'] == ACTIVITY_LIKE) { + if ($b['verb'] == Activity::LIKE) { Logger::log("twitter_post_hook: parameter 2 " . substr($b["thr-parent"], 9), Logger::DEBUG); if ($b['deleted']) { twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike"); @@ -578,14 +586,14 @@ function twitter_post_hook(App $a, array &$b) return; } - Logger::log('twitter post invoked'); + Logger::notice('twitter post invoked', ['id' => $b['id'], 'guid' => $b['guid']]); - PConfig::load($b['uid'], 'twitter'); + DI::pConfig()->load($b['uid'], 'twitter'); $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($b['uid'], 'twitter', 'oauthtoken'); - $osecret = PConfig::get($b['uid'], 'twitter', 'oauthsecret'); + $otoken = DI::pConfig()->get($b['uid'], 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($b['uid'], 'twitter', 'oauthsecret'); if ($ckey && $csecret && $otoken && $osecret) { Logger::log('twitter: we have customer key and oauth stuff, going to send.', Logger::DEBUG); @@ -595,6 +603,10 @@ function twitter_post_hook(App $a, array &$b) return; } + Codebird::setConsumerKey($ckey, $csecret); + $cb = Codebird::getInstance(); + $cb->setToken($otoken, $osecret); + $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret); // Set the timeout for upload to 30 seconds @@ -613,58 +625,68 @@ function twitter_post_hook(App $a, array &$b) $b['body'] = twitter_update_mentions($b['body']); $msgarr = ItemContent::getPlaintextPost($b, $max_char, true, 8); + Logger::info('Got plaintext', ['id' => $b['id'], 'message' => $msgarr]); $msg = $msgarr["text"]; if (($msg == "") && isset($msgarr["title"])) { $msg = Plaintext::shorten($msgarr["title"], $max_char - 50); } - $image = ""; - - if (isset($msgarr["url"]) && ($msgarr["type"] != "photo")) { + if (!empty($msgarr['url']) && ($msgarr['url'] == $b['plink']) && !empty($msgarr['images']) && (count($msgarr['images']) <= 4)) { + $url_added = false; + } elseif (isset($msgarr["url"]) && ($msgarr["type"] != "photo")) { $msg .= "\n" . $msgarr["url"]; $url_added = true; } else { $url_added = false; } - if (isset($msgarr["image"]) && ($msgarr["type"] != "video")) { - $image = $msgarr["image"]; - } - if (empty($msg)) { + Logger::info('Empty message', ['id' => $b['id']]); return; } // and now tweet it :-) $post = []; - if (!empty($image)) { + if (!empty($msgarr['images'])) { + Logger::info('Got images', ['id' => $b['id'], 'images' => $msgarr['images']]); try { - $img_str = Network::fetchUrl($image); + $media_ids = []; + foreach ($msgarr['images'] as $image) { + if (count($media_ids) == 4) { + continue; + } - $tempfile = tempnam(get_temppath(), 'cache'); - file_put_contents($tempfile, $img_str); + $img_str = Network::fetchUrl($image['url']); - $media = $connection->upload('media/upload', ['media' => $tempfile]); + $tempfile = tempnam(get_temppath(), 'cache'); + file_put_contents($tempfile, $img_str); - unlink($tempfile); + Logger::info('Uploading', ['id' => $b['id'], 'image' => $image['url']]); + $media = $connection->upload('media/upload', ['media' => $tempfile]); - if (isset($media->media_id_string)) { - $post['media_ids'] = $media->media_id_string; - } else { - throw new Exception('Failed upload of ' . $image); - } - } catch (Exception $e) { - Logger::log('Exception when trying to send to Twitter: ' . $e->getMessage()); + unlink($tempfile); - // Workaround: Remove the picture link so that the post can be reposted without it - // When there is another url already added, a second url would be superfluous. - if (!$url_added) { - $msg .= "\n" . $image; - } + if (isset($media->media_id_string)) { + $media_ids[] = $media->media_id_string; - $image = ""; + if (!empty($image['description'])) { + $data = ['media_id' => $media->media_id_string, + 'alt_text' => ['text' => substr($image['description'], 0, 420)]]; + $ret = $cb->media_metadata_create($data); + Logger::info('Metadata create', ['id' => $b['id'], 'data' => $data, 'return' => json_encode($ret)]); + } + } else { + throw new Exception('Failed upload', ['id' => $b['id'], 'image' => $image['url']]); + } + } + $post['media_ids'] = implode(',', $media_ids); + if (empty($post['media_ids'])) { + unset($post['media_ids']); + } + } catch (Exception $e) { + Logger::info('Exception when trying to send to Twitter', ['id' => $b['id'], 'message' => $e->getMessage()]); } } @@ -676,26 +698,17 @@ function twitter_post_hook(App $a, array &$b) $url = 'statuses/update'; $result = $connection->post($url, $post); - Logger::log('twitter_post send, result: ' . print_r($result, true), Logger::DEBUG); + Logger::info('twitter_post send', ['id' => $b['id'], 'result' => $result]); if (!empty($result->source)) { Config::set("twitter", "application_name", strip_tags($result->source)); } if (!empty($result->errors)) { - Logger::log('Send to Twitter failed: "' . print_r($result->errors, true) . '"'); - - $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid'])); - if (DBA::isResult($r)) { - $a->contact = $r[0]["id"]; - } - - $s = serialize(['url' => $url, 'item' => $b['id'], 'post' => $post]); - - Queue::add($a->contact, Protocol::TWITTER, $s); - notice(L10n::t('Twitter post failed. Queued for retry.') . EOL); + Logger::info('Send to Twitter failed', ['id' => $b['id'], 'error' => $result->errors]); + Worker::defer(); } elseif ($iscomment) { - Logger::log('twitter_post: Update extid ' . $result->id_str . " for post id " . $b['id']); + Logger::info('Update extid', ['id' => $b['id'], 'extid' => $result->id_str]); Item::update(['extid' => "twitter::" . $result->id_str], ['id' => $b['id']]); } } @@ -734,16 +747,16 @@ function twitter_cron(App $a) if ($last) { $next = $last + ($poll_interval * 60); if ($next > time()) { - Logger::log('twitter: poll intervall not reached'); + Logger::notice('twitter: poll intervall not reached'); return; } } - Logger::log('twitter: cron_start'); + Logger::notice('twitter: cron_start'); $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1'"); if (DBA::isResult($r)) { foreach ($r as $rr) { - Logger::log('twitter: fetching for user ' . $rr['uid']); + Logger::notice('Fetching', ['user' => $rr['uid']]); Worker::add(['priority' => PRIORITY_MEDIUM, 'force_priority' => true], "addon/twitter/twitter_sync.php", 1, (int) $rr['uid']); } } @@ -761,17 +774,17 @@ function twitter_cron(App $a) if ($abandon_days != 0) { $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit); if (!DBA::isResult($user)) { - Logger::log('abandoned account: timeline from user ' . $rr['uid'] . ' will not be imported'); + Logger::notice('abandoned account: timeline from user will not be imported', ['user' => $rr['uid']]); continue; } } - Logger::log('twitter: importing timeline from user ' . $rr['uid']); + Logger::notice('importing timeline', ['user' => $rr['uid']]); Worker::add(['priority' => PRIORITY_MEDIUM, 'force_priority' => true], "addon/twitter/twitter_sync.php", 2, (int) $rr['uid']); /* // To-Do // check for new contacts once a day - $last_contact_check = PConfig::get($rr['uid'],'pumpio','contact_check'); + $last_contact_check = DI::pConfig()->get($rr['uid'],'pumpio','contact_check'); if($last_contact_check) $next_contact_check = $last_contact_check + 86400; else @@ -779,13 +792,13 @@ function twitter_cron(App $a) if($next_contact_check <= time()) { pumpio_getallusers($a, $rr["uid"]); - PConfig::set($rr['uid'],'pumpio','contact_check',time()); + DI::pConfig()->set($rr['uid'],'pumpio','contact_check',time()); } */ } } - Logger::log('twitter: cron_end'); + Logger::notice('twitter: cron_end'); Config::set('twitter', 'last_poll', time()); } @@ -804,17 +817,17 @@ function twitter_expire(App $a) } DBA::close($r); - Logger::log('twitter_expire: expire_start'); + Logger::notice('twitter_expire: expire_start'); $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); if (DBA::isResult($r)) { foreach ($r as $rr) { - Logger::log('twitter_expire: user ' . $rr['uid']); + Logger::notice('twitter_expire', ['user' => $rr['uid']]); Item::expire($rr['uid'], $days, Protocol::TWITTER, true); } } - Logger::log('twitter_expire: expire_end'); + Logger::notice('twitter_expire: expire_end'); } function twitter_prepare_body(App $a, array &$b) @@ -826,7 +839,7 @@ function twitter_prepare_body(App $a, array &$b) if ($b["preview"]) { $max_char = 280; $item = $b["item"]; - $item["plink"] = $a->getBaseURL() . "/display/" . $a->user["nickname"] . "/" . $item["parent"]; + $item["plink"] = DI::baseUrl()->get() . "/display/" . $item["guid"]; $condition = ['uri' => $item["thr-parent"], 'uid' => local_user()]; $orig_post = Item::selectFirst(['author-link'], $condition); @@ -920,14 +933,14 @@ function twitter_fetchtimeline(App $a, $uid) { $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($uid, 'twitter', 'oauthtoken'); - $osecret = PConfig::get($uid, 'twitter', 'oauthsecret'); - $lastid = PConfig::get($uid, 'twitter', 'lastid'); + $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret'); + $lastid = DI::pConfig()->get($uid, 'twitter', 'lastid'); $application_name = Config::get('twitter', 'application_name'); if ($application_name == "") { - $application_name = $a->getHostName(); + $application_name = DI::baseUrl()->getHostname(); } $has_picture = false; @@ -937,7 +950,7 @@ function twitter_fetchtimeline(App $a, $uid) $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret); - $parameters = ["exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true, "tweet_mode" => "extended"]; + $parameters = ["exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true, "tweet_mode" => "extended", "include_ext_alt_text" => true]; $first_time = ($lastid == ""); @@ -948,12 +961,12 @@ function twitter_fetchtimeline(App $a, $uid) try { $items = $connection->get('statuses/user_timeline', $parameters); } catch (TwitterOAuthException $e) { - Logger::log('Error fetching timeline for user ' . $uid . ': ' . $e->getMessage()); + Logger::notice('Error fetching timeline', ['user' => $uid, 'message' => $e->getMessage()]); return; } if (!is_array($items)) { - Logger::log('No items for user ' . $uid, Logger::INFO); + Logger::notice('No items', ['user' => $uid]); return; } @@ -965,7 +978,7 @@ function twitter_fetchtimeline(App $a, $uid) foreach ($posts as $post) { if ($post->id_str > $lastid) { $lastid = $post->id_str; - PConfig::set($uid, 'twitter', 'lastid', $lastid); + DI::pConfig()->set($uid, 'twitter', 'lastid', $lastid); } if ($first_time) { @@ -990,75 +1003,15 @@ function twitter_fetchtimeline(App $a, $uid) } } } - PConfig::set($uid, 'twitter', 'lastid', $lastid); + DI::pConfig()->set($uid, 'twitter', 'lastid', $lastid); Logger::log('Last ID for user ' . $uid . ' is now ' . $lastid, Logger::DEBUG); } -function twitter_queue_hook(App $a) -{ - $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'", - DBA::escape(Protocol::TWITTER) - ); - if (!DBA::isResult($qi)) { - return; - } - - foreach ($qi as $x) { - if ($x['network'] !== Protocol::TWITTER) { - continue; - } - - Logger::log('twitter_queue: run'); - - $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` - WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1", - intval($x['cid']) - ); - if (!DBA::isResult($r)) { - continue; - } - - $user = $r[0]; - - $ckey = Config::get('twitter', 'consumerkey'); - $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($user['uid'], 'twitter', 'oauthtoken'); - $osecret = PConfig::get($user['uid'], 'twitter', 'oauthsecret'); - - $success = false; - - if ($ckey && $csecret && $otoken && $osecret) { - Logger::log('twitter_queue: able to post'); - - $z = unserialize($x['content']); - - $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret); - $result = $connection->post($z['url'], $z['post']); - - Logger::log('twitter_queue: post result: ' . print_r($result, true), Logger::DEBUG); - - if ($result->errors) { - Logger::log('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"'); - } else { - $success = true; - Queue::removeItem($x['id']); - } - } else { - Logger::log("twitter_queue: Error getting tokens for user " . $user['uid']); - } - - if (!$success) { - Logger::log('twitter_queue: delayed'); - Queue::updateTime($x['id']); - } - } -} - function twitter_fix_avatar($avatar) { $new_avatar = str_replace("_normal.", ".", $avatar); - $info = Image::getInfoFromURL($new_avatar); + $info = Images::getInfoFromURLCached($new_avatar); if (!$info) { $new_avatar = $avatar; } @@ -1076,12 +1029,6 @@ function twitter_fetch_contact($uid, $data, $create_user) $url = "https://twitter.com/" . $data->screen_name; $addr = $data->screen_name . "@twitter.com"; - GContact::update(["url" => $url, "network" => Protocol::TWITTER, - "photo" => $avatar, "hide" => true, - "name" => $data->name, "nick" => $data->screen_name, - "location" => $data->location, "about" => $data->description, - "addr" => $addr, "generation" => 2]); - $fields = ['url' => $url, 'network' => Protocol::TWITTER, 'name' => $data->name, 'nick' => $data->screen_name, 'addr' => $addr, 'location' => $data->location, 'about' => $data->description]; @@ -1094,6 +1041,7 @@ function twitter_fetch_contact($uid, $data, $create_user) $contact = DBA::selectFirst('contact', [], ['uid' => $uid, 'alias' => "twitter::" . $data->id_str]); if (!DBA::isResult($contact) && !$create_user) { + Logger::info('User contact not found', ['uid' => $uid, 'twitter-id' => $data->id_str]); return 0; } @@ -1151,8 +1099,8 @@ function twitter_fetchuser(App $a, $uid, $screen_name = "", $user_id = "") { $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($uid, 'twitter', 'oauthtoken'); - $osecret = PConfig::get($uid, 'twitter', 'oauthsecret'); + $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret'); $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", intval($uid)); @@ -1198,7 +1146,7 @@ function twitter_expand_entities(App $a, $body, $item, $picture) $tags_arr = []; foreach ($item->entities->hashtags AS $hashtag) { - $url = '#[url=' . $a->getBaseURL() . '/search?tag=' . $hashtag->text . ']' . $hashtag->text . '[/url]'; + $url = '#[url=' . DI::baseUrl()->get() . '/search?tag=' . $hashtag->text . ']' . $hashtag->text . '[/url]'; $tags_arr['#' . $hashtag->text] = $url; $body = str_replace('#' . $hashtag->text, $url, $body); } @@ -1220,7 +1168,7 @@ function twitter_expand_entities(App $a, $body, $item, $picture) if ($url->url && $url->expanded_url && $url->display_url) { // Quote tweet, we just remove the quoted tweet URL from the body, the share block will be added later. - if (isset($item->quoted_status_id_str) + if (!empty($item->quoted_status) && isset($item->quoted_status_id_str) && substr($url->expanded_url, -strlen($item->quoted_status_id_str)) == $item->quoted_status_id_str ) { $body = str_replace($url->url, '', $body); continue; @@ -1256,7 +1204,7 @@ function twitter_expand_entities(App $a, $body, $item, $picture) } elseif ($oembed_data->type != 'link') { $body = str_replace($url->url, '[url=' . $expanded_url . ']' . $url->display_url . '[/url]', $body); } else { - $img_str = Network::fetchUrl($final_url, true, $redirects, 4); + $img_str = Network::fetchUrl($final_url, true, 4); $tempfile = tempnam(get_temppath(), 'cache'); file_put_contents($tempfile, $img_str); @@ -1331,7 +1279,7 @@ function twitter_expand_entities(App $a, $body, $item, $picture) } $basetag = str_replace('_', ' ', substr($tag, 1)); - $url = '#[url=' . $a->getBaseURL() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]'; + $url = '#[url=' . DI::baseUrl()->get() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]'; $body = str_replace($tag, $url, $body); $tags_arr['#' . $basetag] = $url; } elseif (strpos($tag, '@') === 0) { @@ -1390,13 +1338,25 @@ function twitter_media_entities($post, array &$postarray) } switch ($medium->type) { case 'photo': - $media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]'; - $postarray['object-type'] = ACTIVITY_OBJ_IMAGE; + if (!empty($medium->ext_alt_text)) { + Logger::info('Got text description', ['alt_text' => $medium->ext_alt_text]); + $media[$medium->url] .= "\n[img=" . $medium->media_url_https .']' . $medium->ext_alt_text . '[/img]'; + } else { + $media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]'; + } + + $postarray['object-type'] = Activity\ObjectType::IMAGE; break; case 'video': case 'animated_gif': - $media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]'; - $postarray['object-type'] = ACTIVITY_OBJ_VIDEO; + if (!empty($medium->ext_alt_text)) { + Logger::info('Got text description', ['alt_text' => $medium->ext_alt_text]); + $media[$medium->url] .= "\n[img=" . $medium->media_url_https .']' . $medium->ext_alt_text . '[/img]'; + } else { + $media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]'; + } + + $postarray['object-type'] = Activity\ObjectType::VIDEO; if (is_array($medium->video_info->variants)) { $bitrate = 0; // We take the video with the highest bitrate @@ -1453,15 +1413,15 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl $postarray['thr-parent'] = $parent_item['uri']; $postarray['parent-uri'] = $parent_item['parent-uri']; $postarray['parent'] = $parent_item['parent']; - $postarray['object-type'] = ACTIVITY_OBJ_COMMENT; + $postarray['object-type'] = Activity\ObjectType::COMMENT; } else { $postarray['thr-parent'] = $postarray['uri']; $postarray['parent-uri'] = $postarray['uri']; - $postarray['object-type'] = ACTIVITY_OBJ_NOTE; + $postarray['object-type'] = Activity\ObjectType::NOTE; } // Is it me? - $own_id = PConfig::get($uid, 'twitter', 'own_id'); + $own_id = DI::pConfig()->get($uid, 'twitter', 'own_id'); if ($post->user->id_str == $own_id) { $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", @@ -1482,7 +1442,7 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl $create_user = false; } else { $postarray['parent-uri'] = $postarray['uri']; - $postarray['object-type'] = ACTIVITY_OBJ_NOTE; + $postarray['object-type'] = Activity\ObjectType::NOTE; } if ($contactid == 0) { @@ -1502,7 +1462,7 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl $postarray['contact-id'] = $contactid; - $postarray['verb'] = ACTIVITY_POST; + $postarray['verb'] = Activity::POST; $postarray['author-name'] = $postarray['owner-name']; $postarray['author-link'] = $postarray['owner-link']; $postarray['author-avatar'] = $postarray['owner-avatar']; @@ -1525,7 +1485,7 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl // When the post contains links then use the correct object type if (count($post->entities->urls) > 0) { - $postarray['object-type'] = ACTIVITY_OBJ_BOOKMARK; + $postarray['object-type'] = Activity\ObjectType::BOOKMARK; } // Search for media links @@ -1558,34 +1518,48 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl return []; } - $retweet['source'] = $postarray['source']; - $retweet['private'] = $postarray['private']; - $retweet['allow_cid'] = $postarray['allow_cid']; - $retweet['contact-id'] = $postarray['contact-id']; - $retweet['owner-name'] = $postarray['owner-name']; - $retweet['owner-link'] = $postarray['owner-link']; - $retweet['owner-avatar'] = $postarray['owner-avatar']; + if (!$noquote) { + // Store the original tweet + Item::insert($retweet); - $postarray = $retweet; + // CHange the other post into a reshare activity + $postarray['verb'] = Activity::ANNOUNCE; + $postarray['gravity'] = GRAVITY_ACTIVITY; + $postarray['object-type'] = Activity\ObjectType::NOTE; + + $postarray['thr-parent'] = $retweet['uri']; + $postarray['parent-uri'] = $retweet['uri']; + } else { + $retweet['source'] = $postarray['source']; + $retweet['private'] = $postarray['private']; + $retweet['allow_cid'] = $postarray['allow_cid']; + $retweet['contact-id'] = $postarray['contact-id']; + $retweet['owner-name'] = $postarray['owner-name']; + $retweet['owner-link'] = $postarray['owner-link']; + $retweet['owner-avatar'] = $postarray['owner-avatar']; + + $postarray = $retweet; + } } if (!empty($post->quoted_status) && !$noquote) { $quoted = twitter_createpost($a, $uid, $post->quoted_status, $self, false, false, true); - if (empty($quoted['body'])) { - return []; + if (!empty($quoted['body'])) { + $postarray['body'] .= "\n" . share_header( + $quoted['author-name'], + $quoted['author-link'], + $quoted['author-avatar'], + "", + $quoted['created'], + $quoted['plink'] + ); + + $postarray['body'] .= $quoted['body'] . '[/share]'; + } else { + // Quoted post author is blocked/ignored, so we just provide the link to avoid removing quote context. + $postarray['body'] .= "\n\nhttps://twitter.com/" . $post->quoted_status->user->screen_name . "/status/" . $post->quoted_status->id_str; } - - $postarray['body'] .= "\n" . share_header( - $quoted['author-name'], - $quoted['author-link'], - $quoted['author-avatar'], - "", - $quoted['created'], - $quoted['plink'] - ); - - $postarray['body'] .= $quoted['body'] . '[/share]'; } return $postarray; @@ -1598,7 +1572,7 @@ function twitter_fetchparentposts(App $a, $uid, $post, TwitterOAuth $connection, $posts = []; while (!empty($post->in_reply_to_status_id_str)) { - $parameters = ["trim_user" => false, "tweet_mode" => "extended", "id" => $post->in_reply_to_status_id_str]; + $parameters = ["trim_user" => false, "tweet_mode" => "extended", "id" => $post->in_reply_to_status_id_str, "include_ext_alt_text" => true]; try { $post = $connection->get('statuses/show', $parameters); @@ -1608,7 +1582,7 @@ function twitter_fetchparentposts(App $a, $uid, $post, TwitterOAuth $connection, } if (empty($post)) { - Logger::log("twitter_fetchparentposts: Can't fetch post " . $parameters->id, Logger::DEBUG); + Logger::log("twitter_fetchparentposts: Can't fetch post " . $parameters['id'], Logger::DEBUG); break; } @@ -1630,7 +1604,7 @@ function twitter_fetchparentposts(App $a, $uid, $post, TwitterOAuth $connection, if (!empty($posts)) { foreach ($posts as $post) { - $postarray = twitter_createpost($a, $uid, $post, $self, false, false, false); + $postarray = twitter_createpost($a, $uid, $post, $self, false, !DI::pConfig()->get($uid, 'twitter', 'create_user'), false); if (empty($postarray['body'])) { continue; @@ -1649,17 +1623,17 @@ function twitter_fetchhometimeline(App $a, $uid) { $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($uid, 'twitter', 'oauthtoken'); - $osecret = PConfig::get($uid, 'twitter', 'oauthsecret'); - $create_user = PConfig::get($uid, 'twitter', 'create_user'); - $mirror_posts = PConfig::get($uid, 'twitter', 'mirror_posts'); + $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret'); + $create_user = DI::pConfig()->get($uid, 'twitter', 'create_user'); + $mirror_posts = DI::pConfig()->get($uid, 'twitter', 'mirror_posts'); Logger::log("Fetching timeline for user " . $uid, Logger::DEBUG); $application_name = Config::get('twitter', 'application_name'); if ($application_name == "") { - $application_name = $a->getHostName(); + $application_name = DI::baseUrl()->getHostname(); } $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret); @@ -1688,10 +1662,10 @@ function twitter_fetchhometimeline(App $a, $uid) return; } - $parameters = ["exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true, "tweet_mode" => "extended"]; + $parameters = ["exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true, "tweet_mode" => "extended", "include_ext_alt_text" => true]; //$parameters["count"] = 200; // Fetching timeline - $lastid = PConfig::get($uid, 'twitter', 'lasthometimelineid'); + $lastid = DI::pConfig()->get($uid, 'twitter', 'lasthometimelineid'); $first_time = ($lastid == ""); @@ -1724,7 +1698,7 @@ function twitter_fetchhometimeline(App $a, $uid) foreach ($posts as $post) { if ($post->id_str > $lastid) { $lastid = $post->id_str; - PConfig::set($uid, 'twitter', 'lasthometimelineid', $lastid); + DI::pConfig()->set($uid, 'twitter', 'lasthometimelineid', $lastid); } if ($first_time) { @@ -1769,12 +1743,12 @@ function twitter_fetchhometimeline(App $a, $uid) Logger::log('User ' . $uid . ' posted home timeline item ' . $item); } } - PConfig::set($uid, 'twitter', 'lasthometimelineid', $lastid); + DI::pConfig()->set($uid, 'twitter', 'lasthometimelineid', $lastid); Logger::log('Last timeline ID for user ' . $uid . ' is now ' . $lastid, Logger::DEBUG); // Fetching mentions - $lastid = PConfig::get($uid, 'twitter', 'lastmentionid'); + $lastid = DI::pConfig()->get($uid, 'twitter', 'lastmentionid'); $first_time = ($lastid == ""); @@ -1812,7 +1786,7 @@ function twitter_fetchhometimeline(App $a, $uid) twitter_fetchparentposts($a, $uid, $post, $connection, $self); } - $postarray = twitter_createpost($a, $uid, $post, $self, false, false, false); + $postarray = twitter_createpost($a, $uid, $post, $self, false, !$create_user, false); if (empty($postarray['body'])) { continue; @@ -1824,7 +1798,7 @@ function twitter_fetchhometimeline(App $a, $uid) } } - PConfig::set($uid, 'twitter', 'lastmentionid', $lastid); + DI::pConfig()->set($uid, 'twitter', 'lastmentionid', $lastid); Logger::log('Last mentions ID for user ' . $uid . ' is now ' . $lastid, Logger::DEBUG); } @@ -1833,10 +1807,10 @@ function twitter_fetch_own_contact(App $a, $uid) { $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($uid, 'twitter', 'oauthtoken'); - $osecret = PConfig::get($uid, 'twitter', 'oauthsecret'); + $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret'); - $own_id = PConfig::get($uid, 'twitter', 'own_id'); + $own_id = DI::pConfig()->get($uid, 'twitter', 'own_id'); $contact_id = 0; @@ -1850,7 +1824,7 @@ function twitter_fetch_own_contact(App $a, $uid) return false; } - PConfig::set($uid, 'twitter', 'own_id', $user->id_str); + DI::pConfig()->set($uid, 'twitter', 'own_id', $user->id_str); $contact_id = twitter_fetch_contact($uid, $user, true); } else { @@ -1908,8 +1882,8 @@ function twitter_is_retweet(App $a, $uid, $body) $ckey = Config::get('twitter', 'consumerkey'); $csecret = Config::get('twitter', 'consumersecret'); - $otoken = PConfig::get($uid, 'twitter', 'oauthtoken'); - $osecret = PConfig::get($uid, 'twitter', 'oauthsecret'); + $otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken'); + $osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret'); $connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret); $result = $connection->post('statuses/retweet/' . $id); @@ -1942,7 +1916,7 @@ function twitter_update_mentions($body) function twitter_convert_share(array $attributes, array $author_contact, $content, $is_quote_share) { if ($author_contact['network'] == Protocol::TWITTER) { - $mention = '@' . $author_contact['nickname']; + $mention = '@' . $author_contact['nick']; } else { $mention = $author_contact['addr']; }