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 +4 -4
- data/lib/redd.rb +2 -0
- data/lib/redd/api_client.rb +5 -0
- data/lib/redd/assist/delete_badly_scoring.rb +64 -0
- data/lib/redd/errors.rb +11 -2
- data/lib/redd/models/comment.rb +11 -5
- data/lib/redd/models/listing.rb +7 -0
- data/lib/redd/models/paginated_listing.rb +2 -2
- data/lib/redd/models/postable.rb +5 -0
- data/lib/redd/models/session.rb +3 -3
- data/lib/redd/models/submission.rb +3 -3
- data/lib/redd/models/subreddit.rb +7 -3
- data/lib/redd/models/wiki_page.rb +1 -1
- data/lib/redd/utilities/error_handler.rb +7 -2
- data/lib/redd/version.rb +1 -1
- data/redd.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 278f1a9525f8af1e0b38f8b1e45a7c6ac8115ee6
|
4
|
+
data.tar.gz: afefda832343e71ecf727ed7712d4c33e086ddd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/redd/api_client.rb
CHANGED
@@ -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
|
-
|
16
|
-
|
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
|
|
data/lib/redd/models/comment.rb
CHANGED
@@ -49,7 +49,8 @@ module Redd
|
|
49
49
|
|
50
50
|
# @!attribute [r] replies
|
51
51
|
# @return [Listing<Comment>] the comment replies
|
52
|
-
property :replies,
|
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
|
-
|
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 =
|
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 =
|
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
|
data/lib/redd/models/listing.rb
CHANGED
@@ -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
|
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.
|
110
|
+
@before = listing.empty? ? nil : listing.first.name
|
111
111
|
listing
|
112
112
|
end
|
113
113
|
end
|
data/lib/redd/models/postable.rb
CHANGED
@@ -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)
|
data/lib/redd/models/session.rb
CHANGED
@@ -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'
|
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/
|
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 =
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
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
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.
|
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.
|
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-
|
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:
|
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:
|
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
|