redd 0.8.8 → 0.9.0.pre.1
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/.rubocop.yml +4 -1
- data/CONTRIBUTING.md +63 -0
- data/Guardfile +7 -0
- data/README.md +6 -5
- data/Rakefile +1 -1
- data/TODO.md +423 -0
- data/bin/console +91 -77
- data/bin/guard +2 -0
- data/lib/redd.rb +7 -5
- data/lib/redd/api_client.rb +2 -3
- data/lib/redd/auth_strategies/auth_strategy.rb +7 -2
- data/lib/redd/auth_strategies/script.rb +7 -0
- data/lib/redd/auth_strategies/userless.rb +7 -0
- data/lib/redd/auth_strategies/web.rb +6 -1
- data/lib/redd/client.rb +0 -3
- data/lib/redd/errors.rb +56 -0
- data/lib/redd/middleware.rb +10 -8
- data/lib/redd/models/access.rb +30 -18
- data/lib/redd/models/comment.rb +185 -27
- data/lib/redd/models/front_page.rb +16 -36
- data/lib/redd/models/gildable.rb +1 -1
- data/lib/redd/models/inboxable.rb +13 -3
- data/lib/redd/models/listing.rb +27 -6
- data/lib/redd/models/live_thread.rb +76 -23
- data/lib/redd/models/live_update.rb +46 -0
- data/lib/redd/models/messageable.rb +1 -1
- data/lib/redd/models/mod_action.rb +59 -0
- data/lib/redd/models/model.rb +23 -0
- data/lib/redd/models/moderatable.rb +6 -6
- data/lib/redd/models/modmail.rb +61 -0
- data/lib/redd/models/modmail_conversation.rb +154 -0
- data/lib/redd/models/modmail_message.rb +35 -0
- data/lib/redd/models/more_comments.rb +29 -5
- data/lib/redd/models/multireddit.rb +63 -20
- data/lib/redd/models/paginated_listing.rb +113 -0
- data/lib/redd/models/postable.rb +11 -13
- data/lib/redd/models/private_message.rb +78 -11
- data/lib/redd/models/replyable.rb +2 -2
- data/lib/redd/models/reportable.rb +14 -0
- data/lib/redd/models/searchable.rb +2 -2
- data/lib/redd/models/self.rb +17 -0
- data/lib/redd/models/session.rb +75 -31
- data/lib/redd/models/submission.rb +309 -56
- data/lib/redd/models/subreddit.rb +330 -103
- data/lib/redd/models/trophy.rb +34 -0
- data/lib/redd/models/user.rb +185 -46
- data/lib/redd/models/wiki_page.rb +37 -16
- data/lib/redd/utilities/error_handler.rb +13 -13
- data/lib/redd/utilities/unmarshaller.rb +7 -5
- data/lib/redd/version.rb +1 -1
- data/redd.gemspec +18 -15
- metadata +82 -16
- data/lib/redd/error.rb +0 -53
- data/lib/redd/models/basic_model.rb +0 -80
- data/lib/redd/models/lazy_model.rb +0 -75
- data/lib/redd/models/mod_mail.rb +0 -142
- data/lib/redd/utilities/stream.rb +0 -61
data/lib/redd/models/access.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'model'
|
4
4
|
|
5
5
|
module Redd
|
6
6
|
module Models
|
@@ -8,35 +8,47 @@ module Redd
|
|
8
8
|
# @note This model also supports an additional key, called `:created_at` which is a UNIX time
|
9
9
|
# representing the time the access was created. The default value is the time the object was
|
10
10
|
# initialized.
|
11
|
-
class Access <
|
11
|
+
class Access < Model
|
12
12
|
# Create a non-lazily initialized Access.
|
13
|
-
# @param
|
14
|
-
# @param attributes [Hash] the Access's attributes
|
13
|
+
# @param attributes [Hash] the access's attributes
|
15
14
|
# @example
|
16
15
|
# access = Redd::Models::Access.new(access_token: ...)
|
17
|
-
def initialize(
|
18
|
-
|
19
|
-
|
20
|
-
else
|
21
|
-
super(client, attributes)
|
22
|
-
end
|
16
|
+
def initialize(attributes = {})
|
17
|
+
super(nil, attributes)
|
18
|
+
@creation_time = Time.now
|
23
19
|
end
|
24
20
|
|
21
|
+
# Whether the access has expired.
|
22
|
+
# @param grace_period [Integer] the grace period where the model expires early
|
23
|
+
# @return [Boolean] whether the access has expired
|
25
24
|
def expired?(grace_period = 60)
|
26
|
-
|
27
|
-
return false unless @attributes[:expires_in]
|
28
|
-
Time.now.to_i > @attributes[:created_at] + (@attributes[:expires_in] - grace_period)
|
25
|
+
Time.now > read_attribute(:created_at) + read_attribute(:expires_in) - grace_period
|
29
26
|
end
|
30
27
|
|
28
|
+
# @return [Boolean] whether the access can be refreshed
|
31
29
|
def permanent?
|
32
|
-
|
30
|
+
read_attribute(:refresh_token).nil?
|
33
31
|
end
|
34
32
|
|
35
|
-
|
33
|
+
# @!attribute [r] access_token
|
34
|
+
# @return [String] the access token
|
35
|
+
property :access_token
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
# @!attribute [r] refresh_token
|
38
|
+
# @return [String] the (optional) refresh token
|
39
|
+
property :refresh_token, :nil
|
40
|
+
|
41
|
+
# @!attribute [r] expires_in
|
42
|
+
# @return [Integer] the number of seconds before the access expires
|
43
|
+
property :expires_in
|
44
|
+
|
45
|
+
# @!attribute [r] created_at
|
46
|
+
# @return [Time] the time the access was created
|
47
|
+
property :created_at, default: ->() { @creation_time }
|
48
|
+
|
49
|
+
# @!attribute [r] scope
|
50
|
+
# @return [Array<String>] the scopes that the user is allowed to access
|
51
|
+
property :scope, with: ->(scope) { scope.split(' ') }
|
40
52
|
end
|
41
53
|
end
|
42
54
|
end
|
data/lib/redd/models/comment.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'model'
|
4
4
|
require_relative 'gildable'
|
5
5
|
require_relative 'inboxable'
|
6
6
|
require_relative 'moderatable'
|
7
7
|
require_relative 'postable'
|
8
8
|
require_relative 'replyable'
|
9
|
+
require_relative 'reportable'
|
9
10
|
|
10
11
|
require_relative 'listing'
|
11
12
|
require_relative 'subreddit'
|
@@ -14,47 +15,204 @@ require_relative 'user'
|
|
14
15
|
module Redd
|
15
16
|
module Models
|
16
17
|
# A comment.
|
17
|
-
class Comment <
|
18
|
+
class Comment < Model
|
18
19
|
include Gildable
|
19
20
|
include Inboxable
|
20
21
|
include Moderatable
|
21
22
|
include Postable
|
22
23
|
include Replyable
|
24
|
+
include Reportable
|
23
25
|
|
24
|
-
#
|
25
|
-
#
|
26
|
-
|
27
|
-
# @return [Comment]
|
28
|
-
def self.from_id(client, id)
|
29
|
-
new(client, name: id)
|
30
|
-
end
|
26
|
+
# @!attribute [r] subreddit_id
|
27
|
+
# @return [String] the subreddit fullname
|
28
|
+
property :subreddit_id
|
31
29
|
|
32
|
-
|
30
|
+
# @!attribute [r] approved_at
|
31
|
+
# @return [Time, nil] the time when the comment was approved
|
32
|
+
property :approved_at, from: :approved_at_utc, with: ->(t) { Time.at(t) if t }
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
34
|
+
# @!attribute [r] banned_by
|
35
|
+
# @return [String] the user (?) that banned this comment
|
36
|
+
property :banned_by
|
37
|
+
|
38
|
+
# @!attribute [r] removal_reason
|
39
|
+
# @return [String, nil] the reason for comment removal
|
40
|
+
property :removal_reason
|
41
|
+
|
42
|
+
# @!attribute [r] link
|
43
|
+
# @return [Submission] the link that the comment was posted to
|
44
|
+
property :link, from: :link_id, with: ->(id) { Submission.new(client, name: id) }
|
45
|
+
|
46
|
+
# @!attribute [r] upvoted?
|
47
|
+
# @return [Boolean, nil] whether the user liked/disliked this comment
|
48
|
+
property :upvoted?, from: :likes
|
49
|
+
|
50
|
+
# @!attribute [r] replies
|
51
|
+
# @return [Listing<Comment>] the comment replies
|
52
|
+
property :replies, with: ->(l) { Listing.new(client, l[:data]) if l.is_a?(Hash) }
|
53
|
+
|
54
|
+
# @!attribute [r] user_reports
|
55
|
+
# @return [Array<String>] user reports
|
56
|
+
property :user_reports
|
57
|
+
|
58
|
+
# @!attribute [r] saved?
|
59
|
+
# @return [Boolean] whether the submission was saved by the logged-in user
|
60
|
+
property :saved?, from: :saved
|
61
|
+
|
62
|
+
# @!attribute [r] id
|
63
|
+
# @return [String] the comment id
|
64
|
+
property :id
|
65
|
+
|
66
|
+
# @!attribute [r] banned_at
|
67
|
+
# @return [Time, nil] the time when the comment was banned
|
68
|
+
property :banned_at, from: :banned_at_utc, with: ->(t) { Time.at(t) if t }
|
69
|
+
|
70
|
+
# @!attribute [r] gilded
|
71
|
+
# @return [Integer] the number of times the comment was gilded
|
72
|
+
property :gilded
|
73
|
+
|
74
|
+
# @!attribute [r] archived?
|
75
|
+
# @return [Boolean] whether this comment was archived
|
76
|
+
property :archived?, from: :archived
|
77
|
+
|
78
|
+
# @!attribute [r] report_reasons
|
79
|
+
# @return [Array<String>] report reasons
|
80
|
+
property :report_reasons
|
81
|
+
|
82
|
+
# @!attribute [r] author
|
83
|
+
# @return [User] the comment author
|
84
|
+
property :author, with: ->(name) { User.new(client, name: name) }
|
85
|
+
|
86
|
+
# @!attribute [r] can_mod_post?
|
87
|
+
# @return [Boolean] whether the logged-in user can mod post
|
88
|
+
property :can_mod_post?, from: :can_mod_post
|
89
|
+
|
90
|
+
# @!attribute [r] ups
|
91
|
+
# @return [Integer] the comment upvotes
|
92
|
+
# @deprecated use {#score} instead
|
93
|
+
property :ups
|
94
|
+
|
95
|
+
# @!attribute [r] downs
|
96
|
+
# @return [Integer] the comment downvotes
|
97
|
+
# @deprecated is always 0; use {#score} instead
|
98
|
+
property :downs
|
99
|
+
|
100
|
+
# @!attribute [r] parent
|
101
|
+
# @return [Comment, Submission] the comment parent
|
102
|
+
property :parent,
|
103
|
+
from: :parent_id,
|
104
|
+
with: ->(id) { Session.new(client).from_fullnames(id).first }
|
105
|
+
|
106
|
+
# @!attribute [r] score
|
107
|
+
# @return [Integer] the comment score
|
108
|
+
property :score
|
109
|
+
|
110
|
+
# @!attribute [r] approved_by
|
111
|
+
# @return [String] the user that approved the comment
|
112
|
+
property :approved_by
|
113
|
+
|
114
|
+
# @!attribute [r] body
|
115
|
+
# @return [String] the markdown comment body
|
116
|
+
property :body
|
117
|
+
|
118
|
+
# @!attribute [r] body_html
|
119
|
+
# @return [String] the html-rendered version of the body
|
120
|
+
property :body_html
|
121
|
+
|
122
|
+
# @!attribute [r] edited_at
|
123
|
+
# @return [Time, nil] the time when the comment was edited
|
124
|
+
property :edited_at, from: :edited, with: ->(t) { Time.at(t) if t }
|
125
|
+
|
126
|
+
# @!attribute [r] author_flair_css_class
|
127
|
+
# @return [String] the author flair css class
|
128
|
+
property :author_flair_css_class
|
129
|
+
|
130
|
+
# @!attribute [r] collapsed?
|
131
|
+
# @return [Boolean] whether the comment was collapsed
|
132
|
+
property :collapsed?, from: :collapsed
|
133
|
+
|
134
|
+
# @!attribute [r] submitter?
|
135
|
+
# @return [Boolean] whether the comment author is the link submitter
|
136
|
+
property :submitter?, from: :is_submitter
|
137
|
+
|
138
|
+
# @!attribute [r] collapsed_reason
|
139
|
+
# @return [String] the reason for collapse (?)
|
140
|
+
property :collapsed_reason
|
141
|
+
|
142
|
+
# @!attribute [r] stickied?
|
143
|
+
# @return [Boolean] whether the comment was stickied
|
144
|
+
property :stickied?, from: :stickied
|
145
|
+
|
146
|
+
# @!attribute [r] can_gild?
|
147
|
+
# @return [Boolean] whether the comment is gildable
|
148
|
+
property :can_gild?, from: :can_gild
|
149
|
+
|
150
|
+
# @!attribute [r] subreddit
|
151
|
+
# @return [Subreddit] the comment's subreddit
|
152
|
+
property :subreddit, with: ->(n) { Subreddit.new(client, display_name: n) }
|
153
|
+
|
154
|
+
# @!attribute [r] score_hidden
|
155
|
+
# @return [Boolean] whether the comment score is hidden
|
156
|
+
property :score_hidden?, from: :score_hidden
|
157
|
+
|
158
|
+
# @!attribute [r] subreddit_type
|
159
|
+
# @return [String] subreddit type
|
160
|
+
property :subreddit_type
|
161
|
+
|
162
|
+
# @!attribute [r] name
|
163
|
+
# @return [String] the comment fullname
|
164
|
+
property :name
|
165
|
+
|
166
|
+
# @!attribute [r] author_flair_text
|
167
|
+
# @return [String] the author flair text
|
168
|
+
property :author_flair_text
|
169
|
+
|
170
|
+
# @!attribute [r] created_at
|
171
|
+
# @return [String] the time when the model was created
|
172
|
+
property :created_at, from: :created_utc, with: ->(t) { Time.at(t) }
|
173
|
+
|
174
|
+
# @!attribute [r] subreddit_name_prefixed
|
175
|
+
# @return [String] the subreddit name, prefixed with "r/"
|
176
|
+
property :subreddit_name_prefixed
|
177
|
+
|
178
|
+
# @!attribute [r] controversiality
|
179
|
+
# @return [Integer] the comment controversiality
|
180
|
+
property :controversiality
|
181
|
+
|
182
|
+
# @!attribute [r] depth
|
183
|
+
# @return [Integer] the comment depth
|
184
|
+
property :depth
|
185
|
+
|
186
|
+
# @!attribute [r] mod_reports
|
187
|
+
# @return [Array<String>] the moderator reports
|
188
|
+
property :mod_reports
|
189
|
+
|
190
|
+
# @!attribute [r] report_count
|
191
|
+
# @return [Integer] the report count
|
192
|
+
property :report_count, from: :num_reports
|
193
|
+
|
194
|
+
# @!attribute [r] distinguished?
|
195
|
+
# @return [Boolean] whether the comment is distinguished
|
196
|
+
property :distinguished?, from: :distinguished
|
197
|
+
|
198
|
+
private
|
44
199
|
|
45
|
-
def
|
46
|
-
|
200
|
+
def lazer_reload
|
201
|
+
self[:link] ? load_with_comments : load_without_comments
|
47
202
|
end
|
48
203
|
|
49
204
|
def load_with_comments
|
50
|
-
|
51
|
-
|
52
|
-
|
205
|
+
fully_loaded!
|
206
|
+
id = self[:id] || read_attribute(:name).sub('t1_', '')
|
207
|
+
link_id = read_attribute(:link).name.sub('t3_', '')
|
208
|
+
client.get("/comments/#{link_id}/_/#{id}").body[1][:data][:children][0][:data]
|
53
209
|
end
|
54
210
|
|
55
211
|
def load_without_comments
|
56
|
-
id =
|
57
|
-
|
212
|
+
id = self[:id] || read_attribute(:name).sub('t1_', '')
|
213
|
+
response = client.get('/api/info', id: "t1_#{id}").body[:data][:children][0][:data]
|
214
|
+
response.delete(:replies) # Make sure replies are lazy-loaded later.
|
215
|
+
response
|
58
216
|
end
|
59
217
|
end
|
60
218
|
end
|
@@ -1,41 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '../utilities/stream'
|
3
|
+
require_relative 'model'
|
5
4
|
|
6
5
|
module Redd
|
7
6
|
module Models
|
8
7
|
# The front page.
|
9
8
|
# FIXME: deal with serious code duplication from Subreddit
|
10
|
-
class FrontPage <
|
9
|
+
class FrontPage < Model
|
11
10
|
# @return [Array<String>] reddit's base wiki pages
|
12
11
|
def wiki_pages
|
13
|
-
|
12
|
+
client.get('/wiki/pages').body[:data]
|
14
13
|
end
|
15
14
|
|
16
15
|
# Get a wiki page by its title.
|
17
16
|
# @param title [String] the page's title
|
18
17
|
# @return [WikiPage]
|
19
18
|
def wiki_page(title)
|
20
|
-
WikiPage.new(
|
19
|
+
WikiPage.new(client, title: title)
|
21
20
|
end
|
22
21
|
|
23
22
|
# Get the appropriate listing.
|
24
23
|
# @param sort [:hot, :new, :top, :controversial, :comments, :rising, :gilded] the type of
|
25
24
|
# listing
|
26
|
-
# @param
|
27
|
-
# @option
|
28
|
-
# @option
|
29
|
-
# @option
|
30
|
-
# @option
|
31
|
-
# @option params [:hour, :day, :week, :month, :year, :all] :time the time period to consider
|
25
|
+
# @param options [Hash] a list of options to send with the request
|
26
|
+
# @option options [String] :after return results after the given fullname
|
27
|
+
# @option options [String] :before return results before the given fullname
|
28
|
+
# @option options [Integer, nil] :limit maximum number of items to return (nil for no limit)
|
29
|
+
# @option options [:hour, :day, :week, :month, :year, :all] :time the time period to consider
|
32
30
|
# when sorting.
|
33
31
|
#
|
34
32
|
# @note The option :time only applies to the top and controversial sorts.
|
35
|
-
# @return [
|
36
|
-
def listing(sort, **
|
37
|
-
|
38
|
-
|
33
|
+
# @return [PaginatedListing<Submission>]
|
34
|
+
def listing(sort, **options)
|
35
|
+
options[:t] = options.delete(:time) if options.key?(:time)
|
36
|
+
PaginatedListing.new(client, options) do |**req_options|
|
37
|
+
client.model(:get, "/#{sort}", options.merge(req_options))
|
38
|
+
end
|
39
39
|
end
|
40
40
|
|
41
41
|
# @!method hot(**params)
|
@@ -47,29 +47,9 @@ module Redd
|
|
47
47
|
# @!method gilded(**params)
|
48
48
|
#
|
49
49
|
# @see #listing
|
50
|
-
%i
|
50
|
+
%i[hot new top controversial comments rising gilded].each do |sort|
|
51
51
|
define_method(sort) { |**params| listing(sort, **params) }
|
52
52
|
end
|
53
|
-
|
54
|
-
# Stream newly submitted posts.
|
55
|
-
def post_stream(**params, &block)
|
56
|
-
params[:limit] ||= 100
|
57
|
-
stream = Utilities::Stream.new do |previous|
|
58
|
-
before = previous ? previous.first.name : nil
|
59
|
-
listing(:new, params.merge(before: before))
|
60
|
-
end
|
61
|
-
block_given? ? stream.stream(&block) : stream.enum_for(:stream)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Stream newly submitted comments.
|
65
|
-
def comment_stream(**params, &block)
|
66
|
-
params[:limit] ||= 100
|
67
|
-
stream = Utilities::Stream.new do |previous|
|
68
|
-
before = previous ? previous.first.name : nil
|
69
|
-
listing(:comments, params.merge(before: before))
|
70
|
-
end
|
71
|
-
block_given? ? stream.stream(&block) : stream.enum_for(:stream)
|
72
|
-
end
|
73
53
|
end
|
74
54
|
end
|
75
55
|
end
|
data/lib/redd/models/gildable.rb
CHANGED
@@ -6,17 +6,27 @@ module Redd
|
|
6
6
|
module Inboxable
|
7
7
|
# Block the user that sent this item.
|
8
8
|
def block
|
9
|
-
|
9
|
+
client.post('/api/block', id: read_attribute(:name))
|
10
|
+
end
|
11
|
+
|
12
|
+
# Collapse the item.
|
13
|
+
def collapse
|
14
|
+
client.post('/api/collapse_message', id: read_attribute(:name))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Uncollapse the item.
|
18
|
+
def uncollapse
|
19
|
+
client.post('/api/uncollapse_message', id: read_attribute(:name))
|
10
20
|
end
|
11
21
|
|
12
22
|
# Mark this thing as read.
|
13
23
|
def mark_as_read
|
14
|
-
|
24
|
+
client.post('/api/read_message', id: read_attribute(:name))
|
15
25
|
end
|
16
26
|
|
17
27
|
# Mark one or more messages as unread.
|
18
28
|
def mark_as_unread
|
19
|
-
|
29
|
+
client.post('/api/unread_message', id: read_attribute(:name))
|
20
30
|
end
|
21
31
|
end
|
22
32
|
end
|
data/lib/redd/models/listing.rb
CHANGED
@@ -1,24 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'model'
|
4
4
|
|
5
5
|
module Redd
|
6
6
|
module Models
|
7
7
|
# A backward-expading listing of items.
|
8
8
|
# @see Stream
|
9
|
-
class Listing <
|
9
|
+
class Listing < Model
|
10
10
|
include Enumerable
|
11
11
|
|
12
|
+
# Create a fully initialized listing.
|
13
|
+
# @param client [APIClient] the api client
|
14
|
+
# @param attributes [Hash] the attribute hash
|
15
|
+
def initialize(client, attributes = {})
|
16
|
+
super
|
17
|
+
fully_loaded!
|
18
|
+
end
|
19
|
+
|
12
20
|
# @return [Array<Comment, Submission, PrivateMessage>] an array representation of self
|
13
|
-
def
|
14
|
-
|
21
|
+
def to_a
|
22
|
+
read_attribute(:children)
|
15
23
|
end
|
24
|
+
alias to_ary to_a
|
16
25
|
|
17
|
-
%i
|
26
|
+
%i[[] each empty? first last].each do |method_name|
|
18
27
|
define_method(method_name) do |*args, &block|
|
19
|
-
|
28
|
+
read_attribute(:children).public_send(method_name, *args, &block)
|
20
29
|
end
|
21
30
|
end
|
31
|
+
|
32
|
+
# @!attribute [r] before
|
33
|
+
# @return [String] the fullname of the item before this listing
|
34
|
+
property :before, :nil
|
35
|
+
|
36
|
+
# @!attribute [r] after
|
37
|
+
# @return [String] the fullname of the item that the next listing will start from
|
38
|
+
property :after, :nil
|
39
|
+
|
40
|
+
# @!attribute [r] children
|
41
|
+
# @return [Array<Model>] the listing's children
|
42
|
+
property :children, :required, with: ->(a) { a.map { |m| client.unmarshal(m) } }
|
22
43
|
end
|
23
44
|
end
|
24
45
|
end
|