bad_pigeon 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 342303be8c8d86ebacfe718abecf55e553e1e9528540bac71d82d7f5729e6f84
4
- data.tar.gz: cae73cef84dd9267c5cd79946afc129ccd93c8dcae62127e8c24b4ee88a8c6e6
3
+ metadata.gz: 603fc8ee8a14e3cdeee420620d4457fcd20b3e86c83f104c714848ad384c27b9
4
+ data.tar.gz: 4d342a48e1b0b3478ee16e42ed3826b97f1056e09b53461e9cd2b4c3e3fbd348
5
5
  SHA512:
6
- metadata.gz: d648e2cafc7d9f6b71650dfe283a57dc2a569d9b6da42938b38050559b8c0af0a53917624eb200febdc7a2522bcdfe1242b2645b11532dfcfddf81f408f3ba70
7
- data.tar.gz: f30740c2a5422ada1760dbb46ab66868095546781843d75d3c3f4b1d232994c928835ca78e8812451f71e822503cd0dd2f96cd47f4045c655ee2539d35c242de
6
+ metadata.gz: a70d24323f1b0973bc143e41d07842fce248a25316e24d3e642cea3f862a716867c4728e20254363dbe659d7392235dc40eeac394af09ee986ccc53d303957d9
7
+ data.tar.gz: ee3c1b8e89badd193f386a2f70a3c4642e6e40eb3767d1136f167534003f9cd51de45a2bf46e33de79e60710515f1c7ee45e0072fd5ff85a42b1b610f6b1b517
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.1.2] - 2023-07-11
2
+
3
+ - fixed issue with some requests being ignored because they use POST method
4
+
1
5
  ## [0.1.1] - 2023-06-20
2
6
 
3
7
  - timeline parsing improvements
@@ -2,6 +2,10 @@ module BadPigeon
2
2
  module Component
3
3
  # normal tweet in e.g. home/latest or a user's timeline
4
4
  ORGANIC_FEED_TWEET = "suggest_ranked_organic_tweet"
5
+ NORMAL_TWEET = "tweet"
6
+
7
+ # tweet in the latest timeline
8
+ FOLLOWING = "following_in_network"
5
9
 
6
10
  # tweet in a list timeline
7
11
  ORGANIC_LIST_TWEET = "suggest_organic_list_tweet"
@@ -19,6 +23,7 @@ module BadPigeon
19
23
 
20
24
  # promoted tweet (ad)
21
25
  PROMOTED_TWEET = "suggest_promoted"
26
+ FOLLOWING_PROMOTED = "following_promoted"
22
27
 
23
28
  # "Who to follow" block
24
29
  FOLLOW_SUGGESTIONS = "suggest_who_to_follow"
@@ -2,6 +2,13 @@ require 'bad_pigeon/elements/timeline_tweet'
2
2
  require 'bad_pigeon/util/assertions'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents an "entry" which is a part of a timeline response. An entry in most cases is a wrapper for either one
7
+ # tweet or a group of connected tweets (e.g. a parent and a reply).
8
+ #
9
+ # Tweets can be extracted from an entry using ({#items}) method, which returns an array of tweets as instances of
10
+ # {BadPigeon::TimelineTweet} class.
11
+
5
12
  class TimelineEntry
6
13
  include Assertions
7
14
 
@@ -2,6 +2,12 @@ require 'bad_pigeon/elements/timeline_entry'
2
2
  require 'bad_pigeon/util/assertions'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents an "instruction" which is a part of a timeline response. An instruction can be e.g. pinning one entry
7
+ # to the top or adding some number of entries to the list view.
8
+ #
9
+ # A timeline includes one or more "entries" ({BadPigeon::TimelineEntry}), most of which contain one or more tweets.
10
+
5
11
  class TimelineInstruction
6
12
  include Assertions
7
13
 
@@ -2,6 +2,12 @@ require 'bad_pigeon/models/tweet'
2
2
  require 'bad_pigeon/util/assertions'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents a single tweet in a timeline (may possibly include no data in some cases).
7
+ #
8
+ # Use the {#tweet} method to get an instance of {BadPigeon::Tweet}, which is the final tweet model from which you can
9
+ # extract specific fields or a properly formatted JSON representation.
10
+
5
11
  class TimelineTweet
6
12
  include Assertions
7
13
 
@@ -9,26 +15,8 @@ module BadPigeon
9
15
  @json = json
10
16
  end
11
17
 
12
- def result_type
13
- @json['tweet_results']['result'] && @json['tweet_results']['result']['__typename']
14
- end
15
-
16
- def tweet_data
17
- case result_type
18
- when 'Tweet', 'TweetWithVisibilityResults'
19
- @json['tweet_results']['result']
20
- when 'TweetUnavailable'
21
- nil
22
- when nil
23
- nil
24
- else
25
- assert("Unknown tweet result type: #{result_type}")
26
- nil
27
- end
28
- end
29
-
30
18
  def tweet
31
- tweet_data && Tweet.new(tweet_data)
19
+ @json['tweet_results']['result'] && Tweet.from_result(@json['tweet_results']['result'])
32
20
  end
33
21
  end
34
22
  end
@@ -9,6 +9,8 @@ module BadPigeon
9
9
  case entry.component
10
10
  when Component::ORGANIC_FEED_TWEET,
11
11
  Component::ORGANIC_LIST_TWEET,
12
+ Component::NORMAL_TWEET,
13
+ Component::FOLLOWING,
12
14
  Component::PINNED_TWEET,
13
15
  Component::EXTENDED_REPLY,
14
16
  Component::SOCIAL_CONTEXT,
@@ -17,6 +19,7 @@ module BadPigeon
17
19
  then true
18
20
 
19
21
  when Component::PROMOTED_TWEET,
22
+ Component::FOLLOWING_PROMOTED,
20
23
  Component::FOLLOW_SUGGESTIONS
21
24
  then false
22
25
 
@@ -2,6 +2,11 @@ require_relative 'har_request'
2
2
  require 'json'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents a whole request archive bundle loaded from a *.har file.
7
+ #
8
+ # An archive consists of some number of requests ({BadPigeon::HARRequest}).
9
+
5
10
  class HARArchive
6
11
  def initialize(data)
7
12
  @json = JSON.parse(data)
@@ -2,6 +2,15 @@ require 'addressable/uri'
2
2
  require 'json'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents info about one request and response to it, including the complete response data ({#response_body},
7
+ # or {#response_json} for a parsed JSON form).
8
+ #
9
+ # Requests that may potentially include tweet data return true from the {#includes_tweet_data?} method. The JSON
10
+ # data from each such request represents a "timeline" and may be parsed using a specific timeline class like
11
+ # {BadPigeon::HomeTimeline} or {BadPigeon::UserTimeline}; the {BadPigeon::TIMELINE_TYPES} hash provides a mapping
12
+ # of GraphQL endpoint names to timeline classes, and the endpoint name can be read using {#endpoint_name} method.
13
+
5
14
  class HARRequest
6
15
  def initialize(json)
7
16
  @json = json
@@ -20,7 +29,7 @@ module BadPigeon
20
29
  end
21
30
 
22
31
  def includes_tweet_data?
23
- graphql_endpoint? && method == :get && status == 200 && has_json_response?
32
+ graphql_endpoint? && status == 200 && has_json_response?
24
33
  end
25
34
 
26
35
  def endpoint_name
@@ -6,11 +6,29 @@ require 'bad_pigeon/util/strict_hash'
6
6
  require 'time'
7
7
 
8
8
  module BadPigeon
9
+
10
+ #
11
+ # A model that represents one tweet with an interface matching that from the original `twitter` Ruby gem.
12
+ #
13
+
9
14
  class Tweet
10
15
  include Assertions
16
+ extend Assertions
11
17
 
12
18
  attr_reader :json
13
19
 
20
+ def self.from_result(json)
21
+ case json['__typename']
22
+ when 'Tweet', 'TweetWithVisibilityResults'
23
+ Tweet.new(json)
24
+ when nil, 'TweetUnavailable', 'TweetTombstone'
25
+ nil
26
+ else
27
+ assert("Unknown tweet result type: #{json['__typename']}")
28
+ nil
29
+ end
30
+ end
31
+
14
32
  def initialize(json)
15
33
  case json['__typename']
16
34
  when 'Tweet'
@@ -50,17 +68,17 @@ module BadPigeon
50
68
  alias retweeted_status? retweet?
51
69
 
52
70
  def retweeted_status
53
- legacy['retweeted_status_result'] && Tweet.new(legacy['retweeted_status_result']['result'])
71
+ legacy['retweeted_status_result'] && Tweet.from_result(legacy['retweeted_status_result']['result'])
54
72
  end
55
73
 
56
74
  def quoted_status?
57
75
  # there is also legacy['is_quote_status'], but it may be true while quoted_status_result
58
76
  # is not set if the quoted status was deleted
59
- !!json['quoted_status_result']
77
+ !!quoted_status
60
78
  end
61
79
 
62
80
  def quoted_status
63
- json['quoted_status_result'] && Tweet.new(json['quoted_status_result']['result'])
81
+ json['quoted_status_result'] && Tweet.from_result(json['quoted_status_result']['result'])
64
82
  end
65
83
 
66
84
  alias quoted_tweet? quoted_status?
@@ -1,6 +1,11 @@
1
1
  require 'addressable/uri'
2
2
 
3
3
  module BadPigeon
4
+
5
+ #
6
+ # A model that represents a "URL entity" of a tweet (a shortened link with info about the shortened and original URL).
7
+ #
8
+
4
9
  class URLEntity
5
10
  def initialize(json)
6
11
  @json = json
@@ -1,4 +1,9 @@
1
1
  module BadPigeon
2
+
3
+ #
4
+ # A model that represents a user with an interface matching that from the original `twitter` Ruby gem.
5
+ #
6
+
2
7
  class User
3
8
  attr_reader :json
4
9
 
@@ -2,6 +2,12 @@ require 'bad_pigeon/elements/timeline_instruction'
2
2
  require 'bad_pigeon/util/assertions'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents a timeline response for user's "For You" or "Following" timeline.
7
+ #
8
+ # A timeline includes one or more "instructions" ({BadPigeon::TimelineInstruction}), and usually in particular a
9
+ # "TimelineAddEntries" instruction which provides one or more entries containing tweets.
10
+
5
11
  class HomeTimeline
6
12
  include Assertions
7
13
 
@@ -2,6 +2,12 @@ require 'bad_pigeon/elements/timeline_instruction'
2
2
  require 'bad_pigeon/util/assertions'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents a timeline response for a list timeline.
7
+ #
8
+ # A timeline includes one or more "instructions" ({BadPigeon::TimelineInstruction}), and usually in particular a
9
+ # "TimelineAddEntries" instruction which provides one or more entries containing tweets.
10
+
5
11
  class ListTimeline
6
12
  include Assertions
7
13
 
@@ -2,6 +2,12 @@ require 'bad_pigeon/elements/timeline_instruction'
2
2
  require 'bad_pigeon/util/assertions'
3
3
 
4
4
  module BadPigeon
5
+
6
+ # Represents a timeline response for a timeline of user's posts as seen on their profile page.
7
+ #
8
+ # A timeline includes one or more "instructions" ({BadPigeon::TimelineInstruction}), and usually in particular a
9
+ # "TimelineAddEntries" instruction which provides one or more entries containing tweets.
10
+
5
11
  class UserTimeline
6
12
  include Assertions
7
13
 
@@ -6,6 +6,10 @@ require_relative 'timelines'
6
6
  require 'uri'
7
7
 
8
8
  module BadPigeon
9
+
10
+ # The main entry point to the library. Pass the contents of a HAR archive file to {#get_tweets_from_har} and get
11
+ # a flat list of all extracted tweets in return.
12
+
9
13
  class TweetExtractor
10
14
  include Assertions
11
15
 
@@ -25,7 +29,7 @@ module BadPigeon
25
29
  endpoint = request.endpoint_name
26
30
 
27
31
  if timeline_class = TIMELINE_TYPES[endpoint]
28
- timeline_class.new(request.response_json).instructions.map(&:entries)
32
+ timeline_class.new(request.response_json).instructions.map(&:entries).flatten
29
33
  else
30
34
  debug "Unknown endpoint: #{endpoint}" unless TIMELINE_TYPES.has_key?(endpoint)
31
35
  []
@@ -19,5 +19,9 @@ module BadPigeon
19
19
  end
20
20
  end
21
21
  end
22
+
23
+ def self.extended(target)
24
+ included(target.singleton_class)
25
+ end
22
26
  end
23
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BadPigeon
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bad_pigeon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuba Suder
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-20 00:00:00.000000000 Z
11
+ date: 2023-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable