comparison app/lib/legit_client.rb @ 238:a04b4830eef2 legit-client

Filter out non-own tweets included for replies
author nanaya <me@nanaya.net>
date Sat, 15 Jul 2023 01:53:05 +0900
parents 961d362e42c7
children c454ea4f7b34
comparison
equal deleted inserted replaced
237:961d362e42c7 238:a04b4830eef2
3 resp = fetch("https://twitter.com/i/api/graphql/1-5o8Qhfc2kWlu_2rWNcug/UserTweetsAndReplies?variables=%7B%22userId%22%3A#{escape_param user_id}%2C%22count%22%3A50%2C%22includePromotedContent%22%3Atrue%2C%22withCommunity%22%3Atrue%2C%22withVoice%22%3Atrue%2C%22withV2Timeline%22%3Atrue%7D&features=%7B%22rweb_lists_timeline_redesign_enabled%22%3Atrue%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Afalse%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_media_download_video_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withAuxiliaryUserLabels%22%3Afalse%2C%22withArticleRichContentState%22%3Afalse%7D") 3 resp = fetch("https://twitter.com/i/api/graphql/1-5o8Qhfc2kWlu_2rWNcug/UserTweetsAndReplies?variables=%7B%22userId%22%3A#{escape_param user_id}%2C%22count%22%3A50%2C%22includePromotedContent%22%3Atrue%2C%22withCommunity%22%3Atrue%2C%22withVoice%22%3Atrue%2C%22withV2Timeline%22%3Atrue%7D&features=%7B%22rweb_lists_timeline_redesign_enabled%22%3Atrue%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Afalse%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_media_download_video_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withAuxiliaryUserLabels%22%3Afalse%2C%22withArticleRichContentState%22%3Afalse%7D")
4 4
5 begin 5 begin
6 json = JSON.parse(resp) 6 json = JSON.parse(resp)
7 { 7 {
8 timeline: normalize_timeline(json['data']['user']['result']['timeline_v2']['timeline']['instructions']), 8 timeline: normalize_timeline(json['data']['user']['result']['timeline_v2']['timeline']['instructions'], user_id),
9 raw: resp, 9 raw: resp,
10 } 10 }
11 rescue => e 11 rescue => e
12 return if (json || {}).dig('data').is_a? Hash 12 return if (json || {}).dig('data').is_a? Hash
13 Rails.logger.error("timeline fail: #{user_id}: #{resp}") 13 Rails.logger.error("timeline fail: #{user_id}: #{resp}")
100 end 100 end
101 101
102 ret 102 ret
103 end 103 end
104 104
105 def self.normalize_timeline(json) 105 def self.normalize_timeline(json, user_id)
106 json.find { |instruction| instruction['type'] == 'TimelineAddEntries' }['entries'] 106 json.find { |instruction| instruction['type'] == 'TimelineAddEntries' }['entries']
107 .filter { |entry| entry['entryId'] =~ /\A(profile-conversation|tweet)-/ } 107 .filter { |entry| entry['entryId'] =~ /\A(profile-conversation|tweet)-/ }
108 .reduce([]) do |acc, entry| 108 .reduce([]) do |acc, entry|
109 if entry['content']['entryType'] == 'TimelineTimelineItem' 109 if entry['content']['entryType'] == 'TimelineTimelineItem'
110 acc.push(entry['content']) 110 acc.push(entry['content'])
113 acc.push(item['item']) 113 acc.push(item['item'])
114 end 114 end
115 end 115 end
116 acc 116 acc
117 end.map { |rawTweet| normalize_tweet(rawTweet['itemContent']['tweet_results']['result']) } 117 end.map { |rawTweet| normalize_tweet(rawTweet['itemContent']['tweet_results']['result']) }
118 .filter { |tweet| !tweet.nil? && tweet.dig(:user, :id) == user_id }
118 end 119 end
119 120
120 def self.normalize_tweet(json) 121 def self.normalize_tweet(json)
121 return nil if json.nil? 122 return nil if json.nil?
122 123