]> git.mxchange.org Git - friendica-addons.git/blobdiff - twitter/twitter.php
Move PConfig::set() to DI::pConfig()->set()
[friendica-addons.git] / twitter / twitter.php
index fbe30ce0a3e7806db505248e2098ef4217018eda..fe66886f200733ba272044ff4b4c7abce61203cc 100644 (file)
@@ -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'] .= '<link rel="stylesheet"  type="text/css" href="' . $a->getBaseURL() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
+       DI::page()['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . DI::baseUrl()->get() . '/addon/twitter/twitter.css' . '" media="all" />' . "\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 .= '<div id="twitter-info" >
+                               if (property_exists($details, 'screen_name') &&
+                                   property_exists($details, 'description') &&
+                                   property_exists($details, 'profile_image_url')) {
+                                       $s .= '<div id="twitter-info" >
                                        <p>' . L10n::t('Currently connected to: ') . '<a href="https://twitter.com/' . $details->screen_name . '" target="_twitter">' . $details->screen_name . '</a>
                                                <button type="submit" name="twitter-disconnect" value="1">' . L10n::t('Disconnect') . '</button>
                                        </p>
@@ -364,6 +366,12 @@ function twitter_settings(App $a, &$s)
                                                <em>' . $details->description . '</em>
                                        </p>
                                </div>';
+                               } else {
+                                       $s .= '<div id="twitter-info" >
+                                       <p>Invalid Twitter info</p>
+                               </div>';
+                                       Logger::info('Invalid twitter info (verify credentials).', ['auth' => TwitterOAuth::class]);
+                               }
                                $s .= '<div class="clear"></div>';
 
                                $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,17 +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) . '"');
+                       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']]);
                }
        }
@@ -725,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']);
                }
        }
@@ -752,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
@@ -770,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());
 }
@@ -795,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)
@@ -817,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);
@@ -911,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;
@@ -928,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 == "");
 
@@ -939,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;
        }
 
@@ -956,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) {
@@ -981,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;
        }
@@ -1067,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];
@@ -1085,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;
        }
 
@@ -1142,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));
@@ -1189,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);
        }
@@ -1211,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;
@@ -1247,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);
@@ -1322,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) {
@@ -1381,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
@@ -1444,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",
@@ -1473,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) {
@@ -1493,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'];
@@ -1516,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
@@ -1554,9 +1523,9 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
                        Item::insert($retweet);
 
                        // CHange the other post into a reshare activity
-                       $postarray['verb'] = ACTIVITY2_ANNOUNCE;
+                       $postarray['verb'] = Activity::ANNOUNCE;
                        $postarray['gravity'] = GRAVITY_ACTIVITY;
-                       $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
+                       $postarray['object-type'] = Activity\ObjectType::NOTE;
 
                        $postarray['thr-parent'] = $retweet['uri'];
                        $postarray['parent-uri'] = $retweet['uri'];
@@ -1576,20 +1545,21 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
        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;
@@ -1602,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);
@@ -1612,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;
                }
 
@@ -1634,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;
@@ -1653,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);
@@ -1692,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 == "");
 
@@ -1728,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) {
@@ -1773,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 == "");
 
@@ -1816,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;
@@ -1828,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);
 }
@@ -1837,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;
 
@@ -1854,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 {
@@ -1912,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);
@@ -1946,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'];
        }