redd 0.9.0.pre.2 → 0.9.0.pre.3

Sign up to get free protection for your applications and to get access to all the features.
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