diff options
Diffstat (limited to 'desk/lib/twatter.hoon')
-rw-r--r-- | desk/lib/twatter.hoon | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/desk/lib/twatter.hoon b/desk/lib/twatter.hoon new file mode 100644 index 0000000..3410c0d --- /dev/null +++ b/desk/lib/twatter.hoon @@ -0,0 +1,511 @@ +/- spider +/+ strandio,sr=sortug, server +=, strand=strand:spider +=, strand-fail=strand-fail:libstrand:spider +|% ++$ kv [k=@t v=@t] ++$ param [k=@t v=json] +:: check nitter/src/consts.nim +++ auth-key :: nitters +'Bearer AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF' +++ api-key +'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA' +++ api-key2 +'Bearer AAAAAAAAAAAAAAAAAAAAAIWCCAAAAAAA2C25AxqI%2BYCS7pdfJKRH8Xh19zA%3D8vpDZzPHaEJhd20MKVWp3UR38YoPpuTX7UD2cVYo3YNikubuxd' +:: iphone +++ api-key-3 +'Bearer AAAAAAAAAAAAAAAAAAAAAAj4AQAAAAAAPraK64zCZ9CSzdLesbE7LB%2Bw4uE%3DVJQREvQNCZJNiz3rHO7lOXlkVOQkzzdsgu6wWgcazdMUaGoUGm' +:: ipad -- TimelineSearch returns data in a different format, making nitter return empty results. on the other hand, it has high rate limits. build separate token pools per endpoint? +++ api-key-4 +'Bearer AAAAAAAAAAAAAAAAAAAAAGHtAgAAAAAA%2Bx7ILXNILCqkSGIzy6faIHZ9s3Q%3DQy97w6SIrzE7lQwPJEYQBsArEE2fC25caFwRBvAGi456G09vGR' +++ base-url +"https://api.twitter.com/" +++ graphql +(weld base-url "graphql/") +++ burl +|= [query-id=tape path=tape] ^- tape +"{graphql}{query-id}/{path}" +++ urls + |% + ++ tweet-lurk + [query-id="mbnjGF4gOwo5gyp9pe5s4A" path="TweetResultByRestId"] + :: logged gets + ++ user-by-id + [query-id="oPppcargziU1uDQHAUmH-A" path="UserResultByIdQuery"] + ++ user-tweets + [query-id="VgitpdpNZ-RUIp5D1Z_D-A" path="UserTweets"] + ++ user-by-name + [query-id="G3KGOASz96M-Qu0nwmGXNg" path="UserByScreenName"] + ++ user-replies + [query-id="8IS8MaO-2EN6GZZZb8jF0g" path="UserWithProfileTweetsAndRepliesQueryV2"] + ++ user-media + [query-id="PDfFf8hGeJvUCiTyWtw4wQ" path="MediaTimelineV2"] + ++ conversation + [query-id="83h5UyHZ9wEKBVzALX8R_g" path="ConversationTimelineV2"] + ++ tweet-result + [query-id="sITyJdhRPpvpEjg4waUmTA" path="TweetResultByIdQuery"] + ++ list-by-id + [query-id="iTpgCtbdxrsJfyx0cFjHqg" path="ListByRestId"] + ++ list-by-slug + [query-id="-kmqNvm5Y-cVrfvBy6docg" path="ListBySlug"] + ++ list-members + [query-id="P4NpVZDqUD_7MEM84L-8nw" path="ListMembers"] + ++ list-timeline + [query-id="BbGLL1ZfMibdFNWlk7a0Pw" path="ListTimeline"] + ++ tweet-detail + [query-id="BbmLpxKh8rX8LNe2LhVujA" path="TweetDetail"] + ++ search-timeline + [query-id="lZ0GCEojmtQfiUQa5oJSEw" path="SearchTimeline"] + :: POST URLs + ++ create-tweet + [query-id="5V_dkq1jfalfiFOEZ4g47A" path="CreateTweet"] + ++ create-retweet + [query-id="ojPdsZsimiJrUGLR1sjUtA" path="CreateRetweet"] + ++ favorite-tweet + [query-id="lI07N6Otwv1PhnEgXILM7A" path="FavoriteTweet"] + ++ delete-tweet + [query-id="VaenaVgh5q5ih7kvyVjgtg" path="DeleteTweet"] + -- +:: shit this is legacy +++ notifications-url "https://twitter.com/i/api/2/notifications/all.json" +++ base-variables +^- (list param) +:~ +['includePromotedContent' b+%|] +['withHighlightedLabel' b+%|] +['withCommunity' b+%|] +['with_rux_injections' b+%|] +['referrer' s+'tweet'] +['withTweetQuoteCount' b+%&] +['withBirdwatchNotes' b+%|] +['withBirdwatchPivots' b+%|] +['withTweetResult' b+%&] +['withReactions' b+%&] +['withReactionsMetadata' b+%|] +['withReactionsPerspective' b+%|] +['withSuperFollowsTweetFields' b+%|] +['withSuperFollowsUserFields' b+%|] +['withUserResults' b+%|] +['withVoice' b+%&] +['withV2Timeline' b+%&] +:: +['withQuickPromoteEligibilityTweetFields' b+%|] +== +++ build-variables +|= l=(list param) ^- tape +%- uri-encode:sr +%- trip +%- en:json:html %- pairs:enjs:format +(weld base-variables l) +++ features-2 +^- tape +%- uri-encode:sr +%- trip +%- en:json:html %- pairs:enjs:format +:~ +['android_graphql_skip_api_media_color_palette' b+.n] +['blue_business_profile_image_shape_enabled' b+.n] +['creator_subscriptions_subscription_count_enabled' b+.n] +['creator_subscriptions_tweet_preview_api_enabled' b+.y] +['freedom_of_speech_not_reach_fetch_enabled' b+.n] +['graphql_is_translatable_rweb_tweet_is_translatable_enabled' b+.n] +['hidden_profile_likes_enabled' b+.n] +['highlights_tweets_tab_ui_enabled' b+.n] +['interactive_text_enabled' b+.n] +['longform_notetweets_consumption_enabled' b+.y] +['longform_notetweets_inline_media_enabled' b+.n] +['longform_notetweets_richtext_consumption_enabled' b+.y] +['longform_notetweets_rich_text_read_enabled' b+.n] +['responsive_web_edit_tweet_api_enabled' b+.n] +['responsive_web_home_pinned_timelines_enabled' b+.n] +['responsive_web_enhance_cards_enabled' b+.n] +['responsive_web_graphql_exclude_directive_enabled' b+.y] +['responsive_web_graphql_skip_user_profile_image_extensions_enabled' b+.n] +['responsive_web_graphql_timeline_navigation_enabled' b+.n] +['responsive_web_media_download_video_enabled' b+.n] +['responsive_web_text_conversations_enabled' b+.n] +['responsive_web_twitter_article_tweet_consumption_enabled' b+.n] +['responsive_web_twitter_blue_verified_badge_is_enabled' b+.y] +['rweb_lists_timeline_redesign_enabled' b+.y] +['spaces_2022_h2_clipping' b+.y] +['spaces_2022_h2_spaces_communities' b+.y] +['standardized_nudges_misinfo' b+.n] +['subscriptions_verification_info_enabled' b+.y] +['subscriptions_verification_info_reason_enabled' b+.y] +['subscriptions_verification_info_verified_since_enabled' b+.y] +['super_follow_badge_privacy_enabled' b+.n] +['super_follow_exclusive_tweet_notifications_enabled' b+.n] +['super_follow_tweet_api_enabled' b+.n] +['super_follow_user_api_enabled' b+.n] +['tweet_awards_web_tipping_enabled' b+.n] +['tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled' b+.n] +['tweetypie_unmention_optimization_enabled' b+.n] +['unified_cards_ad_metadata_container_dynamic_card_content_query_enabled' b+.n] +['verified_phone_label_enabled' b+.n] +['vibe_api_enabled' b+.n] +['view_counts_everywhere_api_enabled' b+.n] +== +++ post-features ^- (list [@t json]) +:~ ['c9s_tweet_anatomy_moderator_badge_enabled' b+.y] + ['freedom_of_speech_not_reach_fetch_enabled' b+.y] + ['graphql_is_translatable_rweb_tweet_is_translatable_enabled' b+.y] + ['longform_notetweets_consumption_enabled' b+.y] + ['longform_notetweets_inline_media_enabled' b+.y] + ['longform_notetweets_rich_text_read_enabled' b+.y] + ['responsive_web_edit_tweet_api_enabled' b+.y] + ['responsive_web_enhance_cards_enabled' b+.n] + ['responsive_web_graphql_exclude_directive_enabled' b+.y] + ['responsive_web_graphql_skip_user_profile_image_extensions_enabled' b+.n] + ['responsive_web_graphql_timeline_navigation_enabled' b+.y] + ['responsive_web_home_pinned_timelines_enabled' b+.y] + ['responsive_web_media_download_video_enabled' b+.n] + ['responsive_web_twitter_article_tweet_consumption_enabled' b+.n] + ['standardized_nudges_misinfo' b+.y] + ['tweet_awards_web_tipping_enabled' b+.n] + ['tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled' b+.y] + ['tweetypie_unmention_optimization_enabled' b+.y] + ['verified_phone_label_enabled' b+.n] + ['view_counts_everywhere_api_enabled' b+.y] +== +++ features +^- tape +%- uri-encode:sr +%- trip +%- en:json:html %- pairs:enjs:format +:~ +['creator_subscriptions_tweet_preview_api_enabled' b+%&] +['freedom_of_speech_not_reach_fetch_enabled' b+%|] +['graphql_is_translatable_rweb_tweet_is_translatable_enabled' b+%&] +['interactive_text_enabled' b+%&] +['longform_notetweets_consumption_enabled' b+%&] +['longform_notetweets_inline_media_enabled' b+%&] +['longform_notetweets_rich_text_read_enabled' b+%|] +['longform_notetweets_richtext_consumption_enabled' b+%|] +['responsive_web_edit_tweet_api_enabled' b+%&] +['responsive_web_enhance_cards_enabled' b+%|] +['responsive_web_home_pinned_timelines_enabled' b+.n] +['responsive_web_graphql_exclude_directive_enabled' b+%&] +['responsive_web_graphql_timeline_navigation_enabled' b+%&] +['responsive_web_graphql_skip_user_profile_image_extensions_enabled' b+%|] +['responsive_web_media_download_video_enabled' b+%&] +['responsive_web_text_conversations_enabled' b+%|] +['responsive_web_twitter_blue_verified_badge_is_enabled' b+%&] +['responsive_web_twitter_article_tweet_consumption_enabled' b+%&] +['rweb_lists_timeline_redesign_enabled' b+%&] +['standardized_nudges_misinfo' b+%&] +['tweet_awards_web_tipping_enabled' b+%|] +['tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled' b+%|] +['tweetypie_unmention_optimization_enabled' b+%&] +['verified_phone_label_enabled' b+%|] +['vibe_api_enabled' b+%&] +['view_counts_everywhere_api_enabled' b+%&] +:: +['android_graphql_skip_api_media_color_palette' b+.y] +['super_follow_badge_privacy_enabled' b+.y] +['super_follow_tweet_api_enabled' b+.y] +['subscriptions_verification_info_enabled' b+.y] +['blue_business_profile_image_shape_enabled' b+.n] +['super_follow_user_api_enabled' b+.y] +['creator_subscriptions_subscription_count_enabled' b+.y] +['super_follow_exclusive_tweet_notifications_enabled' b+.y] +['unified_cards_ad_metadata_container_dynamic_card_content_query_enabled' b+.y] +:: logged +['c9s_tweet_anatomy_moderator_badge_enabled' b+.n] +:: logged profile +['hidden_profile_likes_enabled' b+.n] +['subscriptions_verification_info_verified_since_enabled' b+.n] +['highlights_tweets_tab_ui_enabled' b+.n] +['subscriptions_verification_info_is_identity_verified_enabled' b+.n] +['hidden_profile_subscriptions_enabled' b+.n] +== +++ userid-params +|= id=@t ^- tape +=/ vrs (build-variables ~[['userId' %s id] ['count' %n '20']]) +"?variables={vrs}&features={features}" +++ post-params +|= id=@t ^- tape +=/ vrs (build-variables ~[['tweetId' %s id]]) +"?variables={vrs}&features={features}" +++ user-params +|= id=@t ^- tape +=/ vrs (build-variables ~[['screen_name' %s id]]) +"?variables={vrs}&features={features-2}" +++ user-tweets-params +|= id=@t ^- tape +=/ vrs (build-variables ~[['rest_id' %s id] ['count' %n '100']]) +"?variables={vrs}&features={features-2}" +++ legacy-params ^- (list kv) +:~ ['include_profile_interstitial_type' '1'] + ['include_blocking' '1'] + ['include_blocked_by' '1'] + ['include_followed_by' '1'] + ['include_want_retweets' '1'] + ['include_mute_edge' '1'] + ['include_can_dm' '1'] + ['include_can_media_tag' '1'] + ['include_ext_has_nft_avatar' '1'] + ['include_ext_is_blue_verified' '1'] + ['include_ext_verified_type' '1'] + ['skip_status' '1'] + ['cards_platform' 'Web-12'] + ['include_cards' '1'] + ['include_ext_alt_text' 'true'] + ['include_ext_limited_action_results' 'false'] + ['include_quote_count' 'true'] + ['include_reply_count' '1'] + ['tweet_mode' 'extended'] + ['include_ext_collab_control' 'true'] + ['include_ext_views' 'true'] + ['include_entities' 'true'] + ['include_ext_media_color' 'true'] + ['include_ext_media_availability' 'true'] + ['include_ext_sensitive_media_warning' 'true'] + ['include_ext_trusted_friends_metadata' 'true'] + ['send_error_codes' 'true'] + ['simple_quoted_tweet' 'true'] + ['include_ext_edit_control' 'true'] + ['ext' 'mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,enrichments,superFollowMetadata,unmentionInfo,editControl,collab_control,vibe'] +== +++ params-to-tape +|= l=(list kv) ^- tape +%+ roll l +|= [i=[k=@t v=@t] a=tape] +=/ key (trip k.i) +=/ val (trip v.i) +?~ a "{a}key={val}" +"{a}&{key}={val}" +++ notes-params +%- params-to-tape +%+ weld legacy-params +:~ ['count' '20'] + ['requestContext' 'launch'] +== +++ search-params +|= [query=@t cursor=@t count=@ type=?(%user %post)] ^- tape +=/ base=(list kv) %+ weld legacy-params +:~ ['q' query] + ['count' (crip "{<count>}")] + ['query_source' 'typeahead_click'] :: 'typed_query + ['cursor' cursor] + ['pc' '1'] + ['spelling_corrections' '1'] + ['include_ext_edit_control' 'true'] + ['ext' 'mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,enrichments,superFollowMetadata,unmentionInfo,editControl,collab_control,vibe'] +== +=/ full=(list [@t @t]) ?- type + %user (snoc base ['result_filter' 'user']) + %post (snoc base ['tweet_search_mode' 'live']) +== +(params-to-tape full) +:: requests +:: guest-token +++ gt-request +^- request:http +=/ method %'POST' +=/ url 'https://api.twitter.com/1.1/guest/activate.json' +=/ headers +:~ +['connection' 'keep-alive'] +['authorization' auth-key] +['content-type' 'application/x-www-form-urlffencoded'] +['x-twitter-active-user' 'yes'] +['x-twitter-client-language' 'en-us'] +['Accept-language' 'en-US,en;q=0.9'] +['Accept' '*/*'] +['origin' 'https://twitter.com'] +['referer' 'https://twitter.com/'] +['DNT' '1'] +['User-agent' 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'] +== +[method url headers ~] +++ parse-gt +|= res=client-response:iris +=/ m (strand ,cord) +^- form:m +?. ?=(%finished -.res) (strand-fail:strand %no-body ~) +?~ full-file.res (strand-fail:strand %no-body ~) +=/ body=@t q.data.u.full-file.res +=/ jon=(unit json) (de:json:html body) +?~ jon (strand-fail:strand %no-body ~) +(pure:m (parse-gt-json u.jon)) +++ parse-gt-json + |= =json + ^- @t + =/ mapi ((om so):dejs:format json) + (~(got by mapi) 'guest_token') +::: csrf +++ csrf-token-request +^- request:http +=/ method %'GET' +=/ url 'https://developer.twitter.com/en' +=/ headers +:~ +['connection' 'keep-alive'] +['content-type' 'application/x-www-form-urlffencoded'] +['x-twitter-active-user' 'yes'] +['x-twitter-client-language' 'en-us'] +['Accept-language' 'en-US,en;q=0.9'] +['Accept' '*/*'] +['origin' 'https://twitter.com'] +['referer' 'https://twitter.com/'] +['DNT' '1'] +['User-agent' 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'] +== +[method url headers ~] +++ parse-csrf +|= res=client-response:iris +=/ m (strand ,cord) +^- form:m +?. ?=(%finished -.res) (strand-fail:strand %no-body ~) +=+ %+ skim headers.response-header.res +|= pair=[key=@t value=@t] .=(key.pair 'set-cookie') +=/ cookies=(list @t) %+ turn - +|= pair=[key=@t value=@t] value.pair :: parse this a bit +=/ csrf (extract-csrf cookies) +(pure:m csrf) +++ extract-csrf +|= cookies=(list @t) ^- @t +%+ roll cookies +|= [item=@t acc=@t] +?. (contains:string:sr (trip item) "ct0=") acc +=+ (split:string:sr (trip item) ";") +=+ (split:string:sr (snag 0 -) "=") +(crip (snag 1 -)) +:: ++ embed-request +:: |= id=@t +:: =/ endpoint %- crip "{embed-url:endpoints}{(trip id)}" +:: =/ method %'GET' +:: =/ headers +:: :~ +:: ['connection' 'keep-alive'] +:: ['Accept-language' 'en-US,en;q=0.9'] +:: ['Accept' '*/*'] +:: ['origin' 'https://platform.twitter.com'] +:: ['referer' 'https://platform.twitter.com/'] +:: ['DNT' '1'] +:: ['User-agent' 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'] +:: == +:: [method endpoint headers ~] +++ twatter-request +|= [endpoint=@t gt=@t csrf=@t] +^- request:http +=/ method %'GET' +=/ headers +:~ + ['connection' 'keep-alive'] + ['authorization' auth-key] + ['content-type' 'application/json'] + ['x-csrf-token' csrf] + ['x-guest-token' gt] + ['x-twitter-active-user' 'yes'] + ['x-twitter-client-language' 'en-us'] + ['Accept-language' 'en-US,en;q=0.9'] + ['Accept' '*/*'] + ['origin' 'https://twitter.com'] + ['referer' 'https://twitter.com/'] + ['DNT' '1'] + ['User-agent' 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'] + :: ['authority' 'api.twitter.com'] +== +[method endpoint headers ~] +:: ++ build-request-url +:: |= [endpoint=tape l=(list kv)] ^- @t +:: =/ vars (uri-encode:u (build-variables l)) +:: =+ (weld base-url:endpoints endpoint) +:: =+ (weld - vars) +:: (crip -) +:: ++ build-request-v2-url +:: |= [key=@t query=@t cursor=@t endpoint=tape] ^- @t +:: =/ vars (uri-encode:u (build-variables key query cursor)) +:: =/ feat (uri-encode:u features) +:: =+ (weld base-url:endpoiunts endpoint) +:: =+ (weld - vars) +:: =+ (weld - "&features={feat}") +:: (crip -) +:: ++ build-search-url +:: |= [query=@t cursor=@t] :: TODO count and replies +:: :: " include:nativeretweets", " include:replies" +:: =/ q "{<query>} include:nativeretweets exclude:replies" +:: :: =/ q "{<query>} include:nativeretweets include:replies" +:: =/ qq %- en-urlt:html (en-urlt:html q) +:: =/ c %- en-urlt:html (en-urlt:html (trip cursor)) +:: :: =/ c (en-urlt:html (trip cursor)) +:: :: =/ c (trip cursor) +:: =+ +:: "include_profile_interstitial_type=1&cursor={c}&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&include_ext_has_nft_avatar=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_ext_alt_text=true&include_quote_count=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&include_ext_sensitive_media_warning=true&send_error_codes=true&simple_quoted_tweet=true&tweet_search_mode=live&q={qq}&count=20&query_source=typed_query&pc=1&spelling_corrections=1&ext=mediaStats%2ChighlightedLabel%2ChasNftAvatar%2CvoiceInfo%2CsuperFollowMetadata" +:: =+ (weld search:endpoints (search-params query cursor 20 %post)) +:: =+ (weld base-url:endpoints -) +:: (crip -) :: =/ url %- crip (weld tweet-lurk (post-params id.u.command)) + :: =/ req (twatter-request url gt csrf) +++ lurk-headers +:~ + ['Connection' 'keep-alive'] + ['Content-type' 'application/json'] + ['X-twitter-active-user' 'yes'] + ['X-twitter-client-language' 'en-us'] + ::['x-guest-token' gt] + ['Accept-language' 'en-US,en;q=0.9'] + ['Accept' '*/*'] + ['origin' 'https://twitter.com'] + ['referer' 'https://twitter.com/'] + ['DNT' '1'] + ['User-agent' 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'] +== +++ logged-headers +|= [cookie-string=@t csrf=@t] +:: =/ cookie-string +:: 'guest_id=v1%3A169380915299959910; guest_id_ads=v1%3A169380915299959910; guest_id_marketing=v1%3A169380915299959910; kdt=VZeo5b3hVg9VsnQjJYiUsFLNvQIa1lqnxry4dcXK; auth_token=88ab0ccf40bb6be863354720d504895c7308e647; ct0=9469495056e099284acb28c9889422b02285c3883e8c9e681c287c2b1287e2e4a8d0ed3dff9a1db4ef05152d2d3f9a891031804a5fc80ef0d8b0ab251067558eec751d8241f617517a47f12a5c0b1ba8; twid=u%3D1633158398555353096; lang=en; external_referer=8e8t2xd8A2w%3D|0|F8C7rVpldvGNltGxuH%2ByoRY%2FzjrflHIZH061f%2B5OiIwP17ZTz34ZGg%3D%3D; personalization_id="v1_kxI2MM4RPdOgambYqR1tUA=="' +:: =/ csrf '9469495056e099284acb28c9889422b02285c3883e8c9e681c287c2b1287e2e4a8d0ed3dff9a1db4ef05152d2d3f9a891031804a5fc80ef0d8b0ab251067558eec751d8241f617517a47f12a5c0b1ba8' +:~ ['Cookie' cookie-string] + ['Connection' 'keep-alive'] + ['Authorization' api-key] + ['Content-type' 'application/json'] + ['X-csrf-token' csrf] + ['X-twitter-active-user' 'yes'] + ['X-twitter-auth-type' 'OAuth2Session'] + ['X-twitter-client-language' 'en-us'] + ['Accept-language' 'en-US,en;q=0.9'] + ['Accept' '*/*'] + ['origin' 'https://twitter.com'] + ['referer' 'https://twitter.com/'] + ['DNT' '1'] + ['User-agent' 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'] + :: ['authority' 'api.twitter.com'] +== +:: ++ build-post-body +:: |= p=post-req:tw ^- octs +:: =, enjs:format +:: %- json-to-octs:server +:: %- pairs %- limo +:: :~ [%features (pairs post-features)] +:: ['queryId' s+(crip query-id:create-tweet:urls)] +:: ['variables' (pairs (build-post-vars p))] +:: == +:: ++ build-post-vars +:: |= p=post-req:tw +:: =, enjs:format +:: =/ text=@t ?. mask.p text.p +:: :: TODO automated masking +:: 'This tweet is too spicy for Elon Musk\'s friends. To read it click on the link at my bio' +:: =/ base-vars +:: :~ ['dark_request' b+.n] +:: ['semantic_annotation_ids' a+~] +:: ['tweet_text' s+text] +:: :- 'media' %- pairs +:: :~ ['possibly_sensitive' b+.n] +:: ['media_entities' a+~] +:: == +:: == +:: ?^ quoting.p :_ base-vars :- 'attachment_url' s+u.quoting.p +:: ?^ replying.p :_ base-vars :- 'reply' %- pairs +:: :~ ['exclude_reply_user_ids' a+~] +:: ['in_reply_to_twitter_id' s+u.replying.p] +:: == +:: base-vars +:: ++ logged-req ^- request:http +:: =/ url %- crip (weld user-tweets (userid-params '1633158398555353096')) +:: [%'GET' url logged-headers ~] +:: :: {"userId":"1633158398555353096","count":20,"includePromotedContent":true,"withQuickPromoteEligibilityTweetFields":true,"withVoice":true,"withV2Timeline":true} +:: {"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"responsive_web_home_pinned_timelines_enabled":true,"creator_subscriptions_tweet_preview_api_enabled":true,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"c9s_tweet_anatomy_moderator_badge_enabled":true,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_media_download_video_enabled":false,"responsive_web_enhance_cards_enabled":false} + +-- |