-
-
Save salman0ansari/9a3abb134f917ef78a57cf2b313eb433 to your computer and use it in GitHub Desktop.
Get last X tweets from a user.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php declare(strict_types=1); | |
require __DIR__ . '/../vendor/autoload.php'; | |
use GuzzleHttp\Client; | |
use GuzzleHttp\Exception\GuzzleException; | |
use GuzzleHttp\RequestOptions; | |
// ==================== // | |
$username = '@elonmusk'; | |
$count = 2; | |
// $count = 40; // default | |
// ==================== // | |
// From main.HASH.js bundle | |
const AUTH = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA'; | |
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'; | |
$client = new Client([ | |
RequestOptions::PROXY => '', | |
]); | |
// You can refresh these when you get rate-limited | |
$csrfToken = getCsrf(); | |
$guestToken = getGuestToken($client); | |
$userId = getUserId($client, $username, $csrfToken, $guestToken); | |
if($userId === null) | |
{ | |
die("Couldn't find user: $username!" . PHP_EOL); | |
} | |
$startTime = microtime(true); | |
$userTweetsBody = getUserTweets($client, $userId, $count, $csrfToken, $guestToken); | |
$endTime = microtime(true); | |
// $.data.user.result.timeline_v2.timeline.instructions[1].entries[*].content.itemContent.tweet_results.result.legacy.full_text | |
$userTweetsBody = $userTweetsBody['data']['user']['result']['timeline_v2']['timeline']['instructions'][1]['entries']; | |
foreach($userTweetsBody as $entry) | |
{ | |
if($entry['content']['entryType'] === 'TimelineTimelineItem') | |
{ | |
echo html_entity_decode($entry['content']['itemContent']['tweet_results']['result']['legacy']['full_text']) . PHP_EOL; | |
echo '=====' . PHP_EOL; | |
} | |
} | |
$totalTime = round(($endTime - $startTime) * 1000); | |
echo PHP_EOL . "Finished in: {$totalTime}ms" . PHP_EOL; | |
/** | |
* @throws GuzzleException | |
* @throws JsonException | |
*/ | |
function getGuestToken(Client $client) | |
{ | |
$resp = $client->post('https://api.twitter.com/1.1/guest/activate.json', [ | |
RequestOptions::HEADERS => [ | |
'authorization' => 'Bearer ' . AUTH, | |
'user-agent' => USER_AGENT, | |
], | |
]); | |
$body = json_decode($resp->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); | |
return $body['guest_token']; | |
} | |
/** | |
* @throws Exception | |
*/ | |
function getCsrf(): string | |
{ | |
$token = ''; | |
for($i = 0; $i < 32; $i++) | |
{ | |
$value = random_int(0, 255); | |
$token .= substr(dechex($value), -1); | |
} | |
return $token; | |
} | |
/** | |
* @throws GuzzleException | |
* @throws JsonException | |
*/ | |
function getUserId(Client $client, string $username, string $csrfToken, string $guestToken): ?string | |
{ | |
$username = str_replace('@', '', $username); | |
// TODO: Cache | |
$variables = json_encode([ | |
'screen_name' => $username, | |
'withSafetyModeUserFields' => false, | |
'withSuperFollowsUserFields' => false, | |
], JSON_THROW_ON_ERROR); | |
$features = json_encode([ | |
'responsive_web_twitter_blue_verified_badge_is_enabled' => false, | |
'verified_phone_label_enabled' => false, | |
'responsive_web_twitter_blue_new_verification_copy_is_enabled' => false, | |
'responsive_web_graphql_timeline_navigation_enabled' => false, | |
], JSON_THROW_ON_ERROR); | |
// e.exports = { | |
// queryId: "0dbpv_sJc_YxCb-1RO2NFA", | |
// operationName: "UserByScreenName", | |
// operationType: "query", | |
// metadata: { | |
// featureSwitches: ["responsive_web_twitter_blue_verified_badge_is_enabled", "verified_phone_label_enabled", "responsive_web_twitter_blue_new_verification_copy_is_enabled", "responsive_web_graphql_timeline_navigation_enabled"] | |
// } | |
// } | |
$resp = $client->get('https://twitter.com/i/api/graphql/0dbpv_sJc_YxCb-1RO2NFA/UserByScreenName', [ | |
RequestOptions::HEADERS => [ | |
'authorization' => 'Bearer ' . AUTH, | |
'cookie' => "ct0=$csrfToken; gt=$guestToken", | |
'user-agent' => USER_AGENT, | |
'x-csrf-token' => $csrfToken, | |
'x-guest-token' => $guestToken, | |
], | |
RequestOptions::QUERY => [ | |
'variables' => $variables, | |
'features' => $features, | |
], | |
]); | |
$respBody = json_decode($resp->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); | |
if(count($respBody['data']) > 0) | |
{ | |
return $respBody['data']['user']['result']['rest_id']; | |
} | |
return null; | |
} | |
/** | |
* @throws GuzzleException | |
* @throws JsonException | |
*/ | |
function getUserTweets(Client $client, string $userId, int $count, string $csrfToken, string $guestToken): array | |
{ | |
// TODO: Cache | |
$variables = json_encode([ | |
'userId' => $userId, | |
'count' => $count, | |
'includePromotedContent' => false, | |
// 'includePromotedContent' => true, | |
'withQuickPromoteEligibilityTweetFields' => true, | |
'withSuperFollowsUserFields' => true, | |
'withDownvotePerspective' => false, | |
'withReactionsMetadata' => false, | |
'withReactionsPerspective' => false, | |
'withSuperFollowsTweetFields' => true, | |
'withVoice' => true, | |
'withV2Timeline' => true, | |
], JSON_THROW_ON_ERROR); | |
// $features = '{"responsive_web_twitter_blue_verified_badge_is_enabled":true,"verified_phone_label_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"view_counts_public_visibility_enabled":false,"view_counts_everywhere_api_enabled":false,"tweetypie_unmention_optimization_enabled":true,"responsive_web_uc_gql_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"responsive_web_enhance_cards_enabled":true}'; | |
$features = json_encode([ | |
'responsive_web_twitter_blue_verified_badge_is_enabled' => false, | |
'verified_phone_label_enabled' => false, | |
'responsive_web_graphql_timeline_navigation_enabled' => false, | |
'view_counts_public_visibility_enabled' => false, | |
'view_counts_everywhere_api_enabled' => false, | |
'tweetypie_unmention_optimization_enabled' => true, | |
'responsive_web_uc_gql_enabled' => false, | |
'vibe_api_enabled' => false, | |
'responsive_web_edit_tweet_api_enabled' => false, | |
'graphql_is_translatable_rweb_tweet_is_translatable_enabled' => false, | |
'standardized_nudges_misinfo' => false, | |
'tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled' => false, | |
'interactive_text_enabled' => false, | |
'responsive_web_text_conversations_enabled' => false, | |
'responsive_web_enhance_cards_enabled' => false, | |
], JSON_THROW_ON_ERROR); | |
// e.exports = { | |
// queryId: "zEAmYdYUvRuhrr9zjc5Vvg", | |
// operationName: "UserTweets", | |
// operationType: "query", | |
// metadata: { | |
// featureSwitches: ["responsive_web_twitter_blue_verified_badge_is_enabled", "verified_phone_label_enabled", "responsive_web_graphql_timeline_navigation_enabled", "view_counts_public_visibility_enabled", "view_counts_everywhere_api_enabled", "tweetypie_unmention_optimization_enabled", "responsive_web_uc_gql_enabled", "vibe_api_enabled", "responsive_web_edit_tweet_api_enabled", "graphql_is_translatable_rweb_tweet_is_translatable_enabled", "standardized_nudges_misinfo", "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled", "interactive_text_enabled", "responsive_web_text_conversations_enabled", "responsive_web_enhance_cards_enabled"] | |
// } | |
// } | |
$userTweetsResp = $client->get('https://twitter.com/i/api/graphql/zEAmYdYUvRuhrr9zjc5Vvg/UserTweets', [ | |
RequestOptions::HEADERS => [ | |
'authorization' => 'Bearer ' . AUTH, | |
'cookie' => "ct0=$csrfToken; gt=$guestToken", | |
'user-agent' => USER_AGENT, | |
'x-csrf-token' => $csrfToken, | |
'x-guest-token' => $guestToken, | |
], | |
RequestOptions::QUERY => [ | |
'variables' => $variables, | |
'features' => $features, | |
], | |
]); | |
return json_decode($userTweetsResp->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment