Last active
March 21, 2024 05:49
-
-
Save AzureFlow/01cff883b9f1b22e8d0c094df90d0747 to your computer and use it in GitHub Desktop.
Get last N tweets from a user.
This file contains hidden or 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