From 76c38332ee877986f13bd64fc34b4228ee8fccec Mon Sep 17 00:00:00 2001 From: "FiveFilters.org" Date: Mon, 12 Apr 2021 20:08:38 +0200 Subject: [PATCH] [TwitterBridge] Improve timeline processing for username mode (#1946) --- bridges/TwitterBridge.php | 58 +++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/bridges/TwitterBridge.php b/bridges/TwitterBridge.php index c62a6595..30aeb0d6 100644 --- a/bridges/TwitterBridge.php +++ b/bridges/TwitterBridge.php @@ -205,10 +205,22 @@ EOD . urlencode($this->getInput('q')) . '&tweet_mode=extended&tweet_search_mode=live'; case 'By username': - return self::API_URI - . '/2/timeline/profile/' - . $this->getRestId($this->getInput('u')) - . '.json?tweet_mode=extended'; + // use search endpoint if without replies or without retweets enabled + if ($this->getInput('noretweet') || $this->getInput('norep')) { + $query = 'from:' . $this->getInput('u'); + // Twitter's from: search excludes retweets by default + if (!$this->getInput('noretweet')) $query .= ' include:nativeretweets'; + if ($this->getInput('norep')) $query .= ' exclude:replies'; + return self::API_URI + . '/2/search/adaptive.json?q=' + . urlencode($query) + . '&tweet_mode=extended&tweet_search_mode=live'; + } else { + return self::API_URI + . '/2/timeline/profile/' + . $this->getRestId($this->getInput('u')) + . '.json?tweet_mode=extended'; + } case 'By list': return self::API_URI . '/2/timeline/list.json?list_id=' @@ -260,7 +272,35 @@ EOD } } - foreach($data->globalObjects->tweets as $tweet) { + $tweets = array(); + + // Extract tweets from timeline property when in username mode + // This fixes number of issues: + // * If there's a retweet of a quote tweet, the quoted tweet will not appear in results (since it wasn't retweeted directly) + // * Pinned tweets do not get stuck at the bottom + if ($this->queriedContext === 'By username') { + foreach($data->timeline->instructions[0]->addEntries->entries as $tweet) { + if (!isset($tweet->content->item)) continue; + $tweetId = $tweet->content->item->content->tweet->id; + $selectedTweet = $this->getTweet($tweetId, $data->globalObjects); + if (!$selectedTweet) continue; + // If this is a retweet, it will contain shorter text and will point to the original full tweet (retweeted_status_id_str). + // Let's use the original tweet text. + if (isset($selectedTweet->retweeted_status_id_str)) { + $tweetId = $selectedTweet->retweeted_status_id_str; + $selectedTweet = $this->getTweet($tweetId, $data->globalObjects); + if (!$selectedTweet) continue; + } + // use $tweetId as key to avoid duplicates (e.g. user retweeting their own tweet) + $tweets[$tweetId] = $selectedTweet; + } + } else { + foreach($data->globalObjects->tweets as $tweet) { + $tweets[] = $tweet; + } + } + + foreach($tweets as $tweet) { /* Debug::log('>>> ' . json_encode($tweet)); */ // Skip spurious retweets @@ -567,4 +607,12 @@ EOD; } } } + + private function getTweet($tweetId, $apiData) { + if (property_exists($apiData->tweets, $tweetId)) { + return $apiData->tweets->$tweetId; + } else { + return null; + } + } }