redd 0.9.0.pre.2 → 0.9.0.pre.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
  SHA1:
3
- metadata.gz: c5ff3c5e42a08a8f210131a46183225be92037b9
4
- data.tar.gz: 8f85fe4a62585429fae41baad34db469a16f67e4
3
+ metadata.gz: 278f1a9525f8af1e0b38f8b1e45a7c6ac8115ee6
4
+ data.tar.gz: afefda832343e71ecf727ed7712d4c33e086ddd0
5
5
  SHA512:
6
- metadata.gz: b79d75476d5c42f61afc58faad3f634823cde21372545c08dfa087dac3ae2c4cbeda2e6f820acee12e49fd19b198256212537da3fdc71cb4f37a12ab8e9fb2b6
7
- data.tar.gz: dbf251810dd76b1da875f3659bf95243dfdf1e60692bfa891c1dfa18c1c22a3e5959d28bf90066b457c68fcec9139092e7ee3f5857185a947065bc991772de78
6
+ metadata.gz: 4c6db67d5f479822f50b6a5c8d8805d2d0f6993072149f23112c91610a92f47f4072227c15bd3ac40ea0d940284c32064d66d916f3ce92004315ebf63760e173
7
+ data.tar.gz: 81a85e4839ffc3f89e35983fef845314bcae9784ef57de336ef69917a367dbf24aef148d997bd44b1ef9447a7185facd29e68418f3e17e9f252eb7c0896421d3
data/lib/redd.rb CHANGED
@@ -12,6 +12,8 @@ Dir[File.join(__dir__, 'redd', 'auth_strategies', '*.rb')].each { |f| require f
12
12
  require_relative 'redd/errors'
13
13
  # Regular Client
14
14
  require_relative 'redd/api_client'
15
+ # Assists
16
+ Dir[File.join(__dir__, 'redd', 'assist', '*.rb')].each { |f| require f }
15
17
 
16
18
  # Redd is a simple and intuitive API wrapper.
17
19
  module Redd
@@ -15,6 +15,7 @@ module Redd
15
15
  attr_accessor :access
16
16
 
17
17
  # Create a new API client with an auth strategy.
18
+ # TODO: Give user option to pass through all retryable errors.
18
19
  # @param auth [AuthStrategies::AuthStrategy] the auth strategy to use
19
20
  # @param endpoint [String] the API endpoint
20
21
  # @param user_agent [String] the user agent to send
@@ -97,6 +98,10 @@ module Redd
97
98
  raise e if @failures > @max_retries
98
99
  warn "Redd got a #{e.class.name} error (#{e.message}), retrying..."
99
100
  retry
101
+ rescue Errors::RateLimitError => e
102
+ warn "Redd was rate limited for #{e.duration} seconds, waiting..."
103
+ sleep e.duration
104
+ retry
100
105
  else
101
106
  @failures = 0
102
107
  response
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Redd
4
+ module Assist
5
+ # Helper class for deleting negative links or comments. You can run this as a separate process,
6
+ # as long as you set your rate limit time higher in your main process.
7
+ # @note Works with {Submission} and {Comment}.
8
+ # @example
9
+ # assist = Redd::Assist::DeleteBadlyScoring.new(session.client)
10
+ # assist.track(comment)
11
+ # loop do
12
+ # assist.delete_badly_scoring(under_score: 1, minimum_age: 600)
13
+ # sleep 30
14
+ # end
15
+ class DeleteBadlyScoring
16
+ # Create a DeleteBadlyScoring assist.
17
+ # @param client [APIClient] the API client
18
+ def initialize(client)
19
+ @client = client
20
+ @queue = []
21
+ end
22
+
23
+ # Add this model's id to the list of items that are tracked.
24
+ def track(model)
25
+ @queue << model.name
26
+ end
27
+
28
+ # Delete all items that are older than the minimum age and score 0 or below.
29
+ # @param under_score [Integer] the maximum score that the comment must have to be kept
30
+ # @param minimum_age [Integer] the minimum age for deletion (seconds)
31
+ # @return [Array<String>] the deleted item fullnames
32
+ def delete_badly_scoring!(under_score: 1, minimum_age: 15 * 60)
33
+ delete_if do |comment|
34
+ if comment.created_at + minimum_age > Time.now
35
+ :skip
36
+ elsif comment.score < under_score && !comment.deleted? && !comment.archived?
37
+ :delete
38
+ else
39
+ :keep
40
+ end
41
+ end
42
+ end
43
+
44
+ # Delete all items that the block returns true for.
45
+ # @param minimum_age [Integer] the minimum age for deletion
46
+ # @yieldparam comment [Comment] the comment to filter
47
+ # @yieldreturn [:keep, :delete, :skip] whether to keep, delete, or check again later
48
+ # @return [Array<String>] the deleted item fullnames
49
+ def delete_if
50
+ deleted = []
51
+ @queue.delete_if do |fullname|
52
+ comment = Models::Comment.new(@client, name: fullname).reload
53
+ action = yield comment
54
+ if action == :delete
55
+ comment.delete
56
+ deleted << fullname
57
+ end
58
+ action == :keep || action == :delete
59
+ end
60
+ deleted
61
+ end
62
+ end
63
+ end
64
+ end
data/lib/redd/errors.rb CHANGED
@@ -12,8 +12,17 @@ module Redd
12
12
 
13
13
  def initialize(response)
14
14
  @response = response
15
- @name, message = response.body[:json][:errors][0]
16
- super(message)
15
+ super(response.body[:json][:errors][0].join(', '))
16
+ end
17
+ end
18
+
19
+ # Indicates that you were rate limited. This should be taken care of automatically.
20
+ class RateLimitError < APIError
21
+ attr_reader :duration
22
+
23
+ def initialize(response)
24
+ super(response)
25
+ @duration = response.body[:json][:ratelimit]
17
26
  end
18
27
  end
19
28
 
@@ -49,7 +49,8 @@ module Redd
49
49
 
50
50
  # @!attribute [r] replies
51
51
  # @return [Listing<Comment>] the comment replies
52
- property :replies, with: ->(l) { Listing.new(client, l[:data]) if l.is_a?(Hash) }
52
+ property :replies,
53
+ with: ->(r) { r.is_a?(Hash) ? Listing.new(client, r[:data]) : Listing.empty(client) }
53
54
 
54
55
  # @!attribute [r] user_reports
55
56
  # @return [Array<String>] user reports
@@ -63,6 +64,11 @@ module Redd
63
64
  # @return [String] the comment id
64
65
  property :id
65
66
 
67
+ # @!attribute [r] title
68
+ # @return ["comment reply", "post reply", "username mention"] the comment "title" (only
69
+ # visible in messages)
70
+ property :title, :nil
71
+
66
72
  # @!attribute [r] banned_at
67
73
  # @return [Time, nil] the time when the comment was banned
68
74
  property :banned_at, from: :banned_at_utc, with: ->(t) { Time.at(t) if t }
@@ -152,7 +158,7 @@ module Redd
152
158
  property :subreddit, with: ->(n) { Subreddit.new(client, display_name: n) }
153
159
 
154
160
  # @!attribute [r] score_hidden
155
- # @return [Boolean] whether the comment score is hidden
161
+ # @return [Boolean] whether the comment score is hidden
156
162
  property :score_hidden?, from: :score_hidden
157
163
 
158
164
  # @!attribute [r] subreddit_type
@@ -198,18 +204,18 @@ module Redd
198
204
  private
199
205
 
200
206
  def lazer_reload
201
- self[:link] ? load_with_comments : load_without_comments
207
+ exists_locally?(:link) ? load_with_comments : load_without_comments
202
208
  end
203
209
 
204
210
  def load_with_comments
205
211
  fully_loaded!
206
- id = self[:id] || read_attribute(:name).sub('t1_', '')
212
+ id = exists_locally?(:id) ? read_attribute(:id) : read_attribute(:name).sub('t1_', '')
207
213
  link_id = read_attribute(:link).name.sub('t3_', '')
208
214
  client.get("/comments/#{link_id}/_/#{id}").body[1][:data][:children][0][:data]
209
215
  end
210
216
 
211
217
  def load_without_comments
212
- id = self[:id] || read_attribute(:name).sub('t1_', '')
218
+ id = exists_locally?(:id) ? read_attribute(:id) : read_attribute(:name).sub('t1_', '')
213
219
  response = client.get('/api/info', id: "t1_#{id}").body[:data][:children][0][:data]
214
220
  response.delete(:replies) # Make sure replies are lazy-loaded later.
215
221
  response
@@ -9,6 +9,13 @@ module Redd
9
9
  class Listing < Model
10
10
  include Enumerable
11
11
 
12
+ # Create an empty listing with a client.
13
+ # @param client [APIClient] the client to create the listing with
14
+ # @return [Listing] the empty listing
15
+ def self.empty(client)
16
+ Listing.new(client, children: [])
17
+ end
18
+
12
19
  # Create a fully initialized listing.
13
20
  # @param client [APIClient] the api client
14
21
  # @param attributes [Hash] the attribute hash
@@ -68,7 +68,7 @@ module Redd
68
68
 
69
69
  # Go backward through the listing.
70
70
  # @yield [Object] the object returned in the listings
71
- def _stream(&block)
71
+ def _stream
72
72
  buffer = RingBuffer.new(100)
73
73
  remaining = @limit > 0 ? reverse_each.to_a : []
74
74
 
@@ -107,7 +107,7 @@ module Redd
107
107
  # Fetch the previous listing with @caller and update @before.
108
108
  def fetch_prev_listing
109
109
  listing = @caller.call(before: @before, after: nil, limit: 100)
110
- @before = listing.first.name unless listing.empty?
110
+ @before = listing.empty? ? nil : listing.first.name
111
111
  listing
112
112
  end
113
113
  end
@@ -17,6 +17,11 @@ module Redd
17
17
  client.post('/api/del', id: read_attribute(:name))
18
18
  end
19
19
 
20
+ # @return [Boolean] whether the item is probably deleted
21
+ def deleted?
22
+ read_attribute(:author).name == '[deleted]'
23
+ end
24
+
20
25
  # Save a link or comment to the user's account.
21
26
  # @param category [String] a category to save to
22
27
  def save(category = nil)
@@ -101,8 +101,8 @@ module Redd
101
101
 
102
102
  # Return a listing of the user's inbox (including comment replies and private messages).
103
103
  #
104
- # @param category ['inbox', 'unread', 'sent', 'moderator', 'messages'] the category of
105
- # messages to view
104
+ # @param category ['inbox', 'unread', 'sent', 'moderator', 'messages', 'comments',
105
+ # 'selfreply', 'mentions'] the category of messages to view
106
106
  # @param mark [Boolean] whether to remove the orangered from the user's inbox
107
107
  # @param params [Hash] a list of optional params to send with the request
108
108
  # @option params [String] :after return results after the given fullname
@@ -188,7 +188,7 @@ module Redd
188
188
  # "TheGoodPlace"
189
189
  # ],
190
190
  # "comment_count": 176,
191
- # "comment_url": "/r/trendingsubreddits/comments/73dkin/trending_subreddits_for_20170930_rasklibertarians/"
191
+ # "comment_url": "/r/trendingsubreddits/comments/73dkin/trending_subreddits_for_..."
192
192
  # }
193
193
  def trending_subreddits
194
194
  client.get('/api/trending_subreddits').body
@@ -111,7 +111,7 @@ module Redd
111
111
 
112
112
  # @!attribute [r] approved_at
113
113
  # @return [Time, nil] when the submission was last approved
114
- property :approved_at, :approved_at_utc, with: ->(t) { Time.at(t) if t }
114
+ property :approved_at, from: :approved_at_utc, with: ->(t) { Time.at(t) if t }
115
115
 
116
116
  # @!attribute [r] banned_by
117
117
  # @return [String] not sure what this does
@@ -382,11 +382,11 @@ module Redd
382
382
  fully_loaded!
383
383
 
384
384
  # Ensure we have the link's id.
385
- id = self[:id] ? read_attribute(:id) : read_attribute(:name).sub('t3_', '')
385
+ id = exists_locally?(:id) ? read_attribute(:id) : read_attribute(:name).sub('t3_', '')
386
386
 
387
387
  # If a specific sort order was requested, provide it.
388
388
  params = {}
389
- params[:sort] = read_attribute(:sort_order) if self[:sort_order]
389
+ params[:sort] = read_attribute(:sort_order) if exists_locally?(:sort_order)
390
390
 
391
391
  # `response` is a pair (2-element array):
392
392
  # - response[0] is a one-item listing containing the submission
@@ -404,9 +404,13 @@ module Redd
404
404
  property :display_name, :required
405
405
 
406
406
  # @!attribute [r] id
407
- # @return [String] the subreddit's t5_ id.
407
+ # @return [String] the subreddit's base36 id.
408
408
  property :id
409
409
 
410
+ # @!attribute [r] name
411
+ # @return [String] the subreddit's t5_ fullname.
412
+ property :name
413
+
410
414
  # @!attribute [r] title
411
415
  # @return [String] the subreddit's page title text.
412
416
  property :title
@@ -613,7 +617,7 @@ module Redd
613
617
  property :link_flair_enabled?, from: :link_flair_enabled
614
618
 
615
619
  # @!attribute [r] allow_images?
616
- # @return [Boolean] whether images are allowed
620
+ # @return [Boolean] whether images are allowed
617
621
  property :allow_images?, from: :allow_images
618
622
 
619
623
  # @!attribute [r] show_media_preview
@@ -636,7 +640,7 @@ module Redd
636
640
 
637
641
  def lazer_reload
638
642
  fully_loaded!
639
- self[:name] ? load_from_fullname : load_from_display_name
643
+ exists_locally?(:name) ? load_from_fullname : load_from_display_name
640
644
  end
641
645
 
642
646
  # Return the attributes using the display_name (best option).
@@ -48,7 +48,7 @@ module Redd
48
48
  def lazer_reload
49
49
  fully_loaded!
50
50
  path = "/wiki/#{read_attribute(:title)}"
51
- path = "/r/#{read_attribute(:subreddit).display_name}#{path}" if self[:subreddit]
51
+ path = "/r/#{read_attribute(:subreddit).display_name}#{path}" if exists_locally?(:subreddit)
52
52
  client.get(path).body[:data]
53
53
  end
54
54
  end
@@ -29,7 +29,7 @@ module Redd
29
29
  # If there wasn't an status code error and we're allowed to look into the response, parse
30
30
  # it and check for errors.
31
31
  # TODO: deal with errors of type { fields:, explanation:, message:, reason: }
32
- api_error(res)
32
+ rate_limit_error(res) || other_api_error(res)
33
33
  end
34
34
 
35
35
  private
@@ -53,8 +53,13 @@ module Redd
53
53
  HTTP_ERRORS[res.code].new(res) if HTTP_ERRORS.key?(res.code)
54
54
  end
55
55
 
56
+ def rate_limit_error(res)
57
+ return nil unless res.body.is_a?(Hash) && res.body[:json] && res.body[:json][:ratelimit]
58
+ Errors::RateLimitError.new(res)
59
+ end
60
+
56
61
  # Deal with those annoying errors that come with perfect 200 status codes.
57
- def api_error(res)
62
+ def other_api_error(res)
58
63
  return nil unless res.body.is_a?(Hash) && res.body[:json] && res.body[:json][:errors] &&
59
64
  !res.body[:json][:errors].empty?
60
65
  Errors::APIError.new(res)
data/lib/redd/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Redd
4
- VERSION = '0.9.0.pre.2'
4
+ VERSION = '0.9.0.pre.3'
5
5
  end
data/redd.gemspec CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
22
22
  spec.require_paths = ['lib']
23
23
 
24
24
  spec.add_dependency 'http', '~> 2.2'
25
- spec.add_dependency 'lazy_lazer', '~> 0.6'
25
+ spec.add_dependency 'lazy_lazer', '~> 0.8.1'
26
26
 
27
27
  spec.add_development_dependency 'pry', '~> 0.10'
28
28
  spec.add_development_dependency 'bundler', '~> 1.14'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0.pre.2
4
+ version: 0.9.0.pre.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Avinash Dwarapu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-01 00:00:00.000000000 Z
11
+ date: 2017-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.6'
33
+ version: 0.8.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.6'
40
+ version: 0.8.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -216,6 +216,7 @@ files:
216
216
  - bin/setup
217
217
  - lib/redd.rb
218
218
  - lib/redd/api_client.rb
219
+ - lib/redd/assist/delete_badly_scoring.rb
219
220
  - lib/redd/auth_strategies/auth_strategy.rb
220
221
  - lib/redd/auth_strategies/script.rb
221
222
  - lib/redd/auth_strategies/userless.rb