X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=twitter%2Ftwitter.php;h=d846b1ada1afef4f5dd5c7b7ce209942389f1766;hb=f96ff32b212a71aff8f55b4b7b75b6948cbaba2b;hp=320c1cdec431e8b36168f16972d6079502ddee34;hpb=292769e9eafbfd3fbb0e698606ab3710af1cae66;p=friendica-addons.git diff --git a/twitter/twitter.php b/twitter/twitter.php index 320c1cde..d846b1ad 100644 --- a/twitter/twitter.php +++ b/twitter/twitter.php @@ -64,12 +64,13 @@ use Abraham\TwitterOAuth\TwitterOAuth; use Abraham\TwitterOAuth\TwitterOAuthException; +use Codebird\Codebird; use Friendica\App; use Friendica\Content\OEmbed; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\Plaintext; -use Friendica\Core\Addon; use Friendica\Core\Config; +use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Logger; use Friendica\Core\PConfig; @@ -79,13 +80,12 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; 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\ConfigFileLoader; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -97,47 +97,45 @@ define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes function twitter_install() { // we need some hooks, for the configuration and for sending tweets - Addon::registerHook('load_config' , __FILE__, 'twitter_load_config'); - Addon::registerHook('connector_settings' , __FILE__, 'twitter_settings'); - Addon::registerHook('connector_settings_post', __FILE__, 'twitter_settings_post'); - Addon::registerHook('hook_fork' , __FILE__, 'twitter_hook_fork'); - Addon::registerHook('post_local' , __FILE__, 'twitter_post_local'); - Addon::registerHook('notifier_normal' , __FILE__, 'twitter_post_hook'); - Addon::registerHook('jot_networks' , __FILE__, 'twitter_jot_nets'); - Addon::registerHook('cron' , __FILE__, 'twitter_cron'); - Addon::registerHook('queue_predeliver' , __FILE__, 'twitter_queue_hook'); - Addon::registerHook('follow' , __FILE__, 'twitter_follow'); - Addon::registerHook('expire' , __FILE__, 'twitter_expire'); - Addon::registerHook('prepare_body' , __FILE__, 'twitter_prepare_body'); - Addon::registerHook('check_item_notification', __FILE__, 'twitter_check_item_notification'); + Hook::register('load_config' , __FILE__, 'twitter_load_config'); + Hook::register('connector_settings' , __FILE__, 'twitter_settings'); + Hook::register('connector_settings_post', __FILE__, 'twitter_settings_post'); + Hook::register('hook_fork' , __FILE__, 'twitter_hook_fork'); + Hook::register('post_local' , __FILE__, 'twitter_post_local'); + Hook::register('notifier_normal' , __FILE__, 'twitter_post_hook'); + Hook::register('jot_networks' , __FILE__, 'twitter_jot_nets'); + Hook::register('cron' , __FILE__, 'twitter_cron'); + 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"); } function twitter_uninstall() { - Addon::unregisterHook('load_config' , __FILE__, 'twitter_load_config'); - Addon::unregisterHook('connector_settings' , __FILE__, 'twitter_settings'); - Addon::unregisterHook('connector_settings_post', __FILE__, 'twitter_settings_post'); - Addon::unregisterHook('hook_fork' , __FILE__, 'twitter_hook_fork'); - Addon::unregisterHook('post_local' , __FILE__, 'twitter_post_local'); - Addon::unregisterHook('notifier_normal' , __FILE__, 'twitter_post_hook'); - Addon::unregisterHook('jot_networks' , __FILE__, 'twitter_jot_nets'); - Addon::unregisterHook('cron' , __FILE__, 'twitter_cron'); - Addon::unregisterHook('queue_predeliver' , __FILE__, 'twitter_queue_hook'); - Addon::unregisterHook('follow' , __FILE__, 'twitter_follow'); - Addon::unregisterHook('expire' , __FILE__, 'twitter_expire'); - Addon::unregisterHook('prepare_body' , __FILE__, 'twitter_prepare_body'); - Addon::unregisterHook('check_item_notification', __FILE__, 'twitter_check_item_notification'); + Hook::unregister('load_config' , __FILE__, 'twitter_load_config'); + Hook::unregister('connector_settings' , __FILE__, 'twitter_settings'); + Hook::unregister('connector_settings_post', __FILE__, 'twitter_settings_post'); + Hook::unregister('hook_fork' , __FILE__, 'twitter_hook_fork'); + Hook::unregister('post_local' , __FILE__, 'twitter_post_local'); + Hook::unregister('notifier_normal' , __FILE__, 'twitter_post_hook'); + Hook::unregister('jot_networks' , __FILE__, 'twitter_jot_nets'); + Hook::unregister('cron' , __FILE__, 'twitter_cron'); + Hook::unregister('follow' , __FILE__, 'twitter_follow'); + Hook::unregister('expire' , __FILE__, 'twitter_expire'); + Hook::unregister('prepare_body' , __FILE__, 'twitter_prepare_body'); + Hook::unregister('check_item_notification', __FILE__, 'twitter_check_item_notification'); // old setting - remove only - Addon::unregisterHook('post_local_end' , __FILE__, 'twitter_post_hook'); - Addon::unregisterHook('addon_settings' , __FILE__, 'twitter_settings'); - Addon::unregisterHook('addon_settings_post', __FILE__, 'twitter_settings_post'); + Hook::unregister('post_local_end' , __FILE__, 'twitter_post_hook'); + Hook::unregister('addon_settings' , __FILE__, 'twitter_settings'); + Hook::unregister('addon_settings_post', __FILE__, 'twitter_settings_post'); } -function twitter_load_config(App $a) +function twitter_load_config(App $a, ConfigFileLoader $loader) { - $a->loadConfigFile(__DIR__ . '/config/twitter.config.php'); + $a->getConfigCache()->load($loader->loadAddonConfig('twitter')); } function twitter_check_item_notification(App $a, array &$notification_data) @@ -193,21 +191,25 @@ function twitter_follow(App $a, array &$contact) } } -function twitter_jot_nets(App $a, &$b) +function twitter_jot_nets(App $a, array &$jotnets_fields) { if (!local_user()) { return; } - $tw_post = PConfig::get(local_user(), 'twitter', 'post'); - if (intval($tw_post) == 1) { - $tw_defpost = PConfig::get(local_user(), 'twitter', 'post_by_default'); - $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : ''); - $b .= '
' - . L10n::t('Post to Twitter') . '
'; + if (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') + ] + ]; } } + function twitter_settings_post(App $a) { if (!local_user()) { @@ -350,7 +352,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 . '

@@ -359,6 +364,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, [ @@ -573,7 +584,7 @@ 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'); @@ -590,6 +601,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 @@ -608,25 +623,22 @@ 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', $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 (($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)) { return; } @@ -634,32 +646,42 @@ function twitter_post_hook(App $a, array &$b) // and now tweet it :-) $post = []; - if (!empty($image)) { + if (!empty($msgarr['images'])) { try { - $img_str = Network::fetchUrl($image); + $media_ids = []; + foreach ($msgarr['images'] as $image) { + if (count($media_ids) == 4) { + continue; + } + + $img_str = Network::fetchUrl($image['url']); + + $tempfile = tempnam(get_temppath(), 'cache'); + file_put_contents($tempfile, $img_str); - $tempfile = tempnam(get_temppath(), 'cache'); - file_put_contents($tempfile, $img_str); + $media = $connection->upload('media/upload', ['media' => $tempfile]); - $media = $connection->upload('media/upload', ['media' => $tempfile]); + unlink($tempfile); - unlink($tempfile); + if (isset($media->media_id_string)) { + $media_ids[] = $media->media_id_string; - if (isset($media->media_id_string)) { - $post['media_ids'] = $media->media_id_string; - } else { - throw new Exception('Failed upload of ' . $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', ['data' => $data, 'return' => json_encode($ret)]); + } + } else { + throw new Exception('Failed upload of ' . $image['url']); + } + } + $post['media_ids'] = implode(',', $media_ids); + if (empty($post['media_ids'])) { + unset($post['media_ids']); } } catch (Exception $e) { Logger::log('Exception when trying to send to Twitter: ' . $e->getMessage()); - - // 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; - } - - $image = ""; } } @@ -679,16 +701,7 @@ function twitter_post_hook(App $a, array &$b) 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); + Worker::defer(); } elseif ($iscomment) { Logger::log('twitter_post: Update extid ' . $result->id_str . " for post id " . $b['id']); Item::update(['extid' => "twitter::" . $result->id_str], ['id' => $b['id']]); @@ -739,7 +752,7 @@ function twitter_cron(App $a) if (DBA::isResult($r)) { foreach ($r as $rr) { Logger::log('twitter: fetching for user ' . $rr['uid']); - Worker::add(PRIORITY_MEDIUM, "addon/twitter/twitter_sync.php", 1, (int) $rr['uid']); + Worker::add(['priority' => PRIORITY_MEDIUM, 'force_priority' => true], "addon/twitter/twitter_sync.php", 1, (int) $rr['uid']); } } @@ -762,7 +775,7 @@ function twitter_cron(App $a) } Logger::log('twitter: importing timeline from user ' . $rr['uid']); - Worker::add(PRIORITY_MEDIUM, "addon/twitter/twitter_sync.php", 2, (int) $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 @@ -821,7 +834,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"] = $a->getBaseURL() . "/display/" . $item["guid"]; $condition = ['uri' => $item["thr-parent"], 'uid' => local_user()]; $orig_post = Item::selectFirst(['author-link'], $condition); @@ -932,7 +945,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 == ""); @@ -989,66 +1002,6 @@ function twitter_fetchtimeline(App $a, $uid) 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); @@ -1071,12 +1024,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]; @@ -1251,7 +1198,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); @@ -1385,12 +1332,24 @@ function twitter_media_entities($post, array &$postarray) } switch ($medium->type) { case 'photo': - $media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]'; + 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_OBJ_IMAGE; break; case 'video': case 'animated_gif': - $media[$medium->url] .= "\n[img]" . $medium->media_url_https . '[/img]'; + 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_OBJ_VIDEO; if (is_array($medium->video_info->variants)) { $bitrate = 0; @@ -1553,15 +1512,28 @@ 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'] = ACTIVITY2_ANNOUNCE; + $postarray['gravity'] = GRAVITY_ACTIVITY; + $postarray['object-type'] = ACTIVITY_OBJ_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) { @@ -1593,7 +1565,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); @@ -1603,7 +1575,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; } @@ -1683,7 +1655,7 @@ 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'); @@ -1937,7 +1909,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']; }