neonredd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.md +22 -0
  4. data/README.md +1 -0
  5. data/Rakefile +5 -0
  6. data/Redd.LICENSE.md +22 -0
  7. data/RedditKit.LICENSE.md +9 -0
  8. data/lib/redd/access.rb +76 -0
  9. data/lib/redd/clients/base/account.rb +20 -0
  10. data/lib/redd/clients/base/identity.rb +22 -0
  11. data/lib/redd/clients/base/none.rb +27 -0
  12. data/lib/redd/clients/base/privatemessages.rb +33 -0
  13. data/lib/redd/clients/base/read.rb +114 -0
  14. data/lib/redd/clients/base/stream.rb +82 -0
  15. data/lib/redd/clients/base/submit.rb +19 -0
  16. data/lib/redd/clients/base/utilities.rb +143 -0
  17. data/lib/redd/clients/base/wikiread.rb +33 -0
  18. data/lib/redd/clients/base.rb +181 -0
  19. data/lib/redd/clients/installed.rb +56 -0
  20. data/lib/redd/clients/script.rb +40 -0
  21. data/lib/redd/clients/userless.rb +32 -0
  22. data/lib/redd/clients/web.rb +59 -0
  23. data/lib/redd/error.rb +151 -0
  24. data/lib/redd/objects/base.rb +39 -0
  25. data/lib/redd/objects/comment.rb +22 -0
  26. data/lib/redd/objects/labeled_multi.rb +13 -0
  27. data/lib/redd/objects/listing.rb +29 -0
  28. data/lib/redd/objects/more_comments.rb +10 -0
  29. data/lib/redd/objects/private_message.rb +28 -0
  30. data/lib/redd/objects/submission.rb +140 -0
  31. data/lib/redd/objects/subreddit.rb +329 -0
  32. data/lib/redd/objects/thing/editable.rb +22 -0
  33. data/lib/redd/objects/thing/hideable.rb +18 -0
  34. data/lib/redd/objects/thing/inboxable.rb +25 -0
  35. data/lib/redd/objects/thing/messageable.rb +38 -0
  36. data/lib/redd/objects/thing/moderatable.rb +43 -0
  37. data/lib/redd/objects/thing/refreshable.rb +14 -0
  38. data/lib/redd/objects/thing/saveable.rb +21 -0
  39. data/lib/redd/objects/thing/votable.rb +33 -0
  40. data/lib/redd/objects/thing.rb +26 -0
  41. data/lib/redd/objects/user.rb +52 -0
  42. data/lib/redd/objects/wiki_page.rb +15 -0
  43. data/lib/redd/rate_limit.rb +88 -0
  44. data/lib/redd/response/parse_json.rb +18 -0
  45. data/lib/redd/response/raise_error.rb +16 -0
  46. data/lib/redd/version.rb +4 -0
  47. data/lib/redd.rb +50 -0
  48. data/neonredd.gemspec +33 -0
  49. data/spec/redd/objects/base_spec.rb +1 -0
  50. data/spec/redd/response/raise_error_spec.rb +11 -0
  51. data/spec/redd_spec.rb +5 -0
  52. data/spec/spec_helper.rb +71 -0
  53. metadata +225 -0
@@ -0,0 +1,140 @@
1
+ require_relative 'thing'
2
+
3
+ module Redd
4
+ module Objects
5
+ # A submission made in a subreddit.
6
+ class Submission < Thing
7
+ include Thing::Editable
8
+ include Thing::Hideable
9
+ include Thing::Moderatable
10
+ include Thing::Refreshable
11
+ include Thing::Saveable
12
+ include Thing::Votable
13
+
14
+ alias_property :nsfw?, :over_18
15
+ alias_property :self?, :is_self
16
+ alias_property :comments_count, :num_comments
17
+
18
+ # The shorter url for sharing.
19
+ def short_url
20
+ "http://redd.it/#{self[:id]}"
21
+ end
22
+
23
+ # Whether the comment was gilded.
24
+ def gilded?
25
+ self[:gilded] > 0
26
+ end
27
+
28
+ # Mark the thing as Not Suitable For Work.
29
+ def mark_as_nsfw
30
+ get('/api/marknsfw', id: fullname)
31
+ self[:over_18] = true
32
+ end
33
+
34
+ # No longer mark the thing as Not Suitable For Work.
35
+ def unmark_as_nsfw
36
+ get('/api/unmarknsfw', id: fullname)
37
+ self[:over_18] = false
38
+ end
39
+ alias mark_as_safe unmark_as_nsfw
40
+
41
+ # Reply to the thing.
42
+ # @param text [String] The text to comment.
43
+ # @return [Objects::Comment] The reply.
44
+ def add_comment(text)
45
+ client.add_comment(self, text)
46
+ end
47
+
48
+ # Set the submission to "contest mode" (comments are randomly sorted)
49
+ def set_contest_mode
50
+ post('/api/set_contest_mode', id: fullname, state: true)
51
+ end
52
+
53
+ # Unset the "contest mode".
54
+ def unset_contest_mode
55
+ post('/api/set_contest_mode', id: fullname, state: false)
56
+ end
57
+
58
+ # Set the submission as the sticky post of the subreddit
59
+ def set_sticky
60
+ post('/api/set_subreddit_sticky', id: fullname, state: true)
61
+ self[:stickied] = true
62
+ end
63
+
64
+ # Unsticky the post from the subreddit
65
+ def unset_sticky
66
+ post('/api/set_subreddit_sticky', id: fullname, state: false)
67
+ self[:stickied] = false
68
+ end
69
+
70
+ # @return [Listing] The submission's comments.
71
+ # @todo Allow for various depths and contexts and what not. Maybe a
72
+ # get_comment method?
73
+ def comments
74
+ refresh! unless @comments
75
+ @comments
76
+ end
77
+ alias replies comments
78
+
79
+ # Refresh the submission AND its comments.
80
+ # @return [Submission] The updated submission.
81
+ def refresh!
82
+ body = get("/comments/#{id}.json").body
83
+ @comments = client.object_from_body(body[1])
84
+ deep_merge!(body[0])
85
+ end
86
+
87
+ # Take a MoreComments and return a listing of comments.
88
+ # @param [MoreComments] more The object to expand.
89
+ # @return [Listing] A listing of the expanded comments.
90
+ # rubocop:disable Metrics/MethodLength
91
+ def expand_more(more)
92
+ response = client.get(
93
+ '/api/morechildren',
94
+ children: more.join(','),
95
+ link_id: fullname
96
+ )
97
+
98
+ client.object_from_body(
99
+ kind: 'Listing',
100
+ data: {
101
+ before: '', after: '',
102
+ children: response.body[:json][:data][:things]
103
+ }
104
+ )
105
+ end
106
+
107
+ # Get the related articles.
108
+ # @param [Hash] params A list of params to send with the request.
109
+ # @option params [String] :after Return results after the given
110
+ # fullname.
111
+ # @option params [String] :before Return results before the given
112
+ # fullname.
113
+ # @option params [Integer] :count The number of items already seen
114
+ # in the listing.
115
+ # @option params [1..100] :limit The maximum number of things to
116
+ # return.
117
+ # @return [Objects::Listing<Objects::Thing>]
118
+ def get_related(**params)
119
+ related = get("/related/#{id}.json", params).body[1]
120
+ client.object_from_body(related)
121
+ end
122
+
123
+ # Get other articles with the same URL.
124
+ # @param [Hash] params A list of params to send with the request.
125
+ # @option params [String] :after Return results after the given
126
+ # fullname.
127
+ # @option params [String] :before Return results before the given
128
+ # fullname.
129
+ # @option params [Integer] :count The number of items already seen
130
+ # in the listing.
131
+ # @option params [1..100] :limit The maximum number of things to
132
+ # return.
133
+ # @return [Objects::Listing<Objects::Submission>]
134
+ def get_duplicates(**params)
135
+ duplicates = get("/duplicates/#{id}.json", params).body[1]
136
+ client.object_from_body(duplicates)
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,329 @@
1
+ require 'fastimage'
2
+ require_relative 'thing'
3
+ # rubocop:disable Metrics/ClassLength, Metrics/ParameterLists
4
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
5
+ module Redd
6
+ module Objects
7
+ # A comment made on links.
8
+ # @todo #subscribe! and #unsubscribe!
9
+ class Subreddit < Thing
10
+ include Thing::Messageable
11
+ include Thing::Refreshable
12
+
13
+ alias_property :header_image, :header_img
14
+ alias_property :nsfw?, :over18
15
+ alias_property :users_online, :accounts_active
16
+ alias_property :type, :subreddit_type
17
+ alias_property :times_gilded, :gilded
18
+
19
+ # @!group Submissions
20
+
21
+ # Submit a link or a text post to the subreddit.
22
+ #
23
+ # @param [String] title The title of the submission.
24
+ # @param [String] captcha A possible captcha result to send if one
25
+ # is required.
26
+ # @param [String] identifier The identifier for the captcha if one
27
+ # is required.
28
+ # @param [String] text The text of the self-post.
29
+ # @param [String] url The URL of the link.
30
+ # @param [Boolean] resubmit Whether to post a link to the subreddit
31
+ # despite it having been posted there before (you monster).
32
+ # @param [Boolean] sendreplies Whether to send the replies to your
33
+ # inbox.
34
+ # @return [Objects::Thing] The returned result (url, id and name).
35
+ def submit(
36
+ title, captcha = nil, identifier = nil, text: nil, url: nil,
37
+ resubmit: false, sendreplies: true
38
+ )
39
+
40
+ params = {
41
+ extension: 'json', title: title, sr: display_name,
42
+ resubmit: resubmit, sendreplies: sendreplies
43
+ }
44
+
45
+ if captcha
46
+ params[:captcha] = captcha
47
+ params[:iden] = identifier
48
+ end
49
+ if text
50
+ params[:kind] = :self
51
+ params[:text] = text
52
+ end
53
+ if url
54
+ params[:kind] = :link
55
+ params[:url] = url
56
+ end
57
+
58
+ response = post('/api/submit', params)
59
+ Objects::Thing.new(self, response.body[:json][:data])
60
+ end
61
+
62
+ # Add a comment to the submission.
63
+ # @param text [String] The text to comment.
64
+ # @return [Objects::Comment] The reply.
65
+ def add_comment(text)
66
+ client.add_comment(self, text)
67
+ end
68
+
69
+ # @!endgroup
70
+
71
+ # @!group Stylesheets
72
+
73
+ # @return [String] The url for the subreddit's stylesheet.
74
+ def stylesheet_url
75
+ get("/r/#{display_name}/stylesheet").headers['location']
76
+ end
77
+
78
+ # @return [String] The css for the subreddit.
79
+ def stylesheet
80
+ Faraday.get(stylesheet_url).body
81
+ end
82
+
83
+ # Edit the subreddit's stylesheet
84
+ #
85
+ # @param [String] contents The new CSS.
86
+ # @param [String] reason Why you modified the stylesheet.
87
+ # @author Takashi M (@beatak) and Avinash Dwarapu (@avidw)
88
+ # @note https://www.reddit.com/r/naut/about/stylesheet/ is a good place
89
+ # to test if you have an error.
90
+ def edit_stylesheet(contents, reason = nil)
91
+ params = { op: 'save', stylesheet_contents: contents }
92
+ params[:reason] = reason if reason
93
+ post("/r/#{display_name}/api/subreddit_stylesheet", params)
94
+ end
95
+
96
+ # @!endgroup
97
+
98
+ # @!group Invites
99
+
100
+ # Accept a moderator invite from a subreddit.
101
+ def accept_moderator_invite!
102
+ post("/r/#{display_name}/api/accept_moderator_invite")
103
+ end
104
+
105
+ # Stop being a contributor of the subreddit.
106
+ def leave_contributor_status!
107
+ post('/api/leavecontributor', id: fullname)
108
+ end
109
+
110
+ # Stop being a moderator of the subreddit.
111
+ def leave_moderator_status!
112
+ post('/api/leavemoderator', id: fullname)
113
+ end
114
+
115
+ # @!endgroup
116
+
117
+ # @!group Flairs
118
+
119
+ # Get a list of everbody on the subreddit with a user flair.
120
+ #
121
+ # @param [Hash] params A list of params to send with the request.
122
+ # @option params [String] :after Return results after the given
123
+ # fullname.
124
+ # @option params [String] :before Return results before the given
125
+ # fullname.
126
+ # @option params [Integer] :count The number of items already seen in the
127
+ # listing.
128
+ # @option params [1..1000] :limit The maximum number of things to
129
+ # return.
130
+ # @option params [String] :name The username when getting the flair of
131
+ # just one user.
132
+ # @return [Objects::Listing<Hash>] A listing of flair hashes.
133
+ def get_flairlist(**params)
134
+ body = get("/r/#{display_name}/api/flairlist.json", params).body
135
+ client.object_from_body(
136
+ kind: 'Listing',
137
+ data: {
138
+ children: body[:users],
139
+ before: body[:prev],
140
+ after: body[:next]
141
+ }
142
+ )
143
+ end
144
+
145
+ # Get the flair of a user.
146
+ #
147
+ # @param [Objects::User, String] user The user to find.
148
+ # @return [Hash, nil] Flair info about the user or nil if nobody was
149
+ # found.
150
+ def get_flair(user)
151
+ username = client.property(user, :name)
152
+ flair = get_flairlist(user: username).first
153
+ flair if flair[:user].casecmp(username) == 0
154
+ end
155
+
156
+ # Set the flair of a user or link.
157
+ # @param [Objects::Subreddit, Objects::User] thing The user or link to
158
+ # set the flair to.
159
+ # @param [:user, :link] type The type of thing.
160
+ # @param [String] text The text to set the flair to.
161
+ # @param [String] css_class The css_class of the flair.
162
+ def set_flair(thing, type = nil, text = nil, css_class = nil)
163
+ params = { text: text, css_class: css_class }
164
+ if thing.is_a?(Objects::User) || type == :user
165
+ params[:name] = client.property(thing, :name)
166
+ elsif thing.is_a?(Objects::Submission) || type == :link
167
+ params[:link] = client.property(thing, :fullname)
168
+ else
169
+ fail 'You should provide a proper type.'
170
+ end
171
+
172
+ post("/r/#{display_name}/api/flair", params)
173
+ end
174
+
175
+ # @!endgroup
176
+
177
+ # @!group Listings
178
+
179
+ # @!method get_hot(**params)
180
+ # @!method get_new(**params)
181
+ # @!method get_top(**params)
182
+ # @!method get_controversial(**params)
183
+ # @!method get_comments(**params)
184
+ #
185
+ # Get the appropriate listing.
186
+ # @param params [Hash] A list of params to send with the request.
187
+ # @option params [String] :after Return results after the given
188
+ # fullname.
189
+ # @option params [String :before Return results before the given
190
+ # fullname.
191
+ # @option params [Integer] :count (0) The number of items already seen
192
+ # in the listing.
193
+ # @option params [1..100] :limit (25) The maximum number of things to
194
+ # return.
195
+ # @option params [:hour, :day, :week, :month, :year, :all] :t The
196
+ # time period to consider when sorting.
197
+ #
198
+ # @note The option :t only applies to the top and controversial sorts.
199
+ # @return [Objects::Listing<Objects::Thing>]
200
+ %w(hot new top controversial comments).each do |sort|
201
+ define_method :"get_#{sort}" do |**params|
202
+ client.send(:"get_#{sort}", self, **params)
203
+ end
204
+ end
205
+
206
+ # Search.
207
+ # @param query [String] The query string.
208
+ # @param params [Hash] A list of params to send with the request.
209
+ # @option params [String] :after Return results after the given
210
+ # fullname.
211
+ # @option params [String :before Return results before the given
212
+ # fullname.
213
+ # @option params [Integer] :count The number of items already seen in
214
+ # the listing.
215
+ # @option params [1..100] :limit The maximum number of things to
216
+ # return.
217
+ # @option params [:cloudsearch, :lucene, :plain] :syntax The type of
218
+ # syntax to use.
219
+ # @option params [:relevance, :new, :hot, :top, :comments] :sort The
220
+ # way to sort the results.
221
+ # @option params [:hour, :day, :week, :month, :year, :all] :t The
222
+ # time period to consider when sorting.
223
+ #
224
+ # @note The option :t only applies to the top and controversial sorts.
225
+ # @return [Objects::Listing<Objects::Thing>]
226
+ def search(query, **params)
227
+ client.search(query, self, **params)
228
+ end
229
+
230
+ # @!endgroup
231
+
232
+ # @!group Moderation
233
+
234
+ # @!method get_reports(**params)
235
+ # @!method get_spam(**params)
236
+ # @!method get_modqueue(**params)
237
+ # @!method get_unmoderated(**params)
238
+ # @!method get_edited(**params)
239
+ #
240
+ # Get the appropriate moderator listing.
241
+ # @param [Hash] params A list of params to send with the request.
242
+ # @option params [String] :after Return results after the given
243
+ # fullname.
244
+ # @option params [String] :before Return results before the given
245
+ # fullname.
246
+ # @option params [Integer] :count The number of items already seen
247
+ # in the listing.
248
+ # @option params [1..100] :limit The maximum number of things to
249
+ # return.
250
+ # @option params :location No idea what this does.
251
+ # @option params [:links, :comments] :only The type of things to show.
252
+ #
253
+ # @return [Objects::Listing<Objects::Thing>]
254
+ # @see https://www.reddit.com/dev/api#GET_about_{location}
255
+ %w(reports spam modqueue unmoderated edited).each do |sort|
256
+ define_method :"get_#{sort}" do |**params|
257
+ client.request_object(
258
+ :get, "/r/#{display_name}/about/#{sort}", params
259
+ )
260
+ end
261
+ end
262
+
263
+ # @return [Objects::Base] The current settings of a subreddit.
264
+ def admin_about
265
+ client.request_object(:get, "/r/#{display_name}/about/edit.json")
266
+ end
267
+
268
+ # Edit the subreddit's settings
269
+ # @param [Hash] attributes The subreddit's new settings.
270
+ # @note This method may make additional requests if not all of the
271
+ # required attributes are provided. Take a look at the source for the
272
+ # required attributes required to avoid making the additional request.
273
+ # @see https://github.com/alaycock/MeetCal-bot/blob/master/serverInfo.conf
274
+ def admin_edit(attributes)
275
+ params = {
276
+ # Subreddit name
277
+ sr: fullname,
278
+ # Apparently useless options
279
+ show_cname_sidebar: true,
280
+ :"header-title" => title
281
+ }
282
+
283
+ required = %i(
284
+ allow_top collapse_deleted_comments comment_score_hide_mins
285
+ css_on_cname description exclude_banned_modqueue lang link_type name
286
+ over_18 public_description public_traffic show_media spam_comments
287
+ spam_links spam_selfposts submit_link_label submit_text
288
+ submit_text_label title type wiki_edit_age wiki_edit_karma wikimode
289
+ )
290
+
291
+ if required.all? { |key| attributes.key?(key) }
292
+ params.merge!(attributes)
293
+ else
294
+ about = admin_about
295
+ final = about
296
+ .select { |k, _| required.include?(k) }
297
+ .merge(
298
+ name: display_name,
299
+ type: about[:subreddit_type],
300
+ lang: about[:language],
301
+ link_type: about[:content_options],
302
+ allow_top: true,
303
+ css_on_cname: true
304
+ )
305
+ .merge(attributes)
306
+ params.merge!(final)
307
+ end
308
+
309
+ post('/api/site_admin', params)
310
+ end
311
+
312
+ # Add or replace the subreddit image or header logo.
313
+ # @param [String, IO] file The path/url to the file or the file itself.
314
+ # @param [String] name The name of the uploaded file.
315
+ # @return [String] The url of the image on reddit's CDN.
316
+ def upload_image(file, name = nil)
317
+ io = (file.is_a?(IO) ? file : File.open(file, 'r'))
318
+ type = FastImage.type(io)
319
+ payload = Faraday::UploadIO.new(io, "image/#{type}")
320
+
321
+ params = { file: payload, header: (name ? 0 : 1), img_type: type }
322
+ params[:name] = name if name
323
+ post("/r/#{display_name}/api/upload_sr_img", params).body[:img_src]
324
+ end
325
+
326
+ # @!endgroup
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,22 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that can be edited and deleted.
5
+ module Editable
6
+ # Edit a thing.
7
+ # @param text [String] The new text.
8
+ # @return [Thing] The edited thing.
9
+ def edit(text)
10
+ post('/api/editusertext', thing_id: fullname, text: text)
11
+ self[(is_a?(Submission) ? :selftext : :body)] = text
12
+ self
13
+ end
14
+
15
+ # Delete the thing
16
+ def delete!
17
+ post('/api/del', id: fullname)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that can be hidden from the user.
5
+ module Hideable
6
+ # Hide a link from the user.
7
+ def hide
8
+ post('/api/hide', id: fullname)
9
+ end
10
+
11
+ # Unhide a previously hidden link.
12
+ def unhide
13
+ post('/api/unhide', id: fullname)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that can be sent to a user's inbox.
5
+ module Inboxable
6
+ # Mark this thing as read.
7
+ def mark_as_read
8
+ post('/api/read_message', id: fullname)
9
+ end
10
+
11
+ # Mark one or more messages as unread.
12
+ def mark_as_unread
13
+ post('/api/unread_message', id: fullname)
14
+ end
15
+
16
+ # Reply to the thing.
17
+ # @param text [String] The text to comment.
18
+ # @return [Objects::Comment, Objects::PrivateMessage] The reply.
19
+ def reply(text)
20
+ client.add_comment(self, text)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that can be sent a message.
5
+ module Messageable
6
+ # Compose a message to a person or the moderators of a subreddit.
7
+ #
8
+ # @param [String] subject The subject of the message.
9
+ # @param [String] text The message text.
10
+ # @param [String] from_sr The subreddit to send the message on behalf
11
+ # of or nil if from the user.
12
+ # @param [String] captcha A possible captcha result to send if one
13
+ # is required.
14
+ # @param [String] identifier The identifier for the captcha if one
15
+ # is required.
16
+ # rubocop:disable Metrics/MethodLength
17
+ def send_message(
18
+ subject, text, from_sr = nil, captcha = nil, identifier = nil
19
+ )
20
+ params = { subject: subject, text: text }
21
+ if captcha
22
+ params[:captcha] = captcha
23
+ params[:iden] = identifier
24
+ end
25
+ params[:from_sr] = client.property(from_sr, :display_name) if from_sr
26
+ params[:to] =
27
+ if respond_to?(:display_name)
28
+ "/r/#{self[:display_name]}"
29
+ else
30
+ self[:name]
31
+ end
32
+
33
+ post('/api/compose', params)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,43 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that a moderator can manage.
5
+ module Moderatable
6
+ # Approve a submission.
7
+ def approve!
8
+ post('/api/approve', id: fullname)
9
+ end
10
+
11
+ # Remove a submission.
12
+ # @param [Boolean] spam Whether or not this item is removed due to it
13
+ # being spam.
14
+ def remove!(spam = false)
15
+ post('/api/remove', id: fullname, spam: spam)
16
+ end
17
+
18
+ # Distinguish a link or comment with a sigil to show that it has
19
+ # been created by a moderator.
20
+ # @param [:yes, :no, :admin, :special] how How to distinguish the
21
+ # thing.
22
+ def distinguish(how = :yes)
23
+ post('/api/distinguish', id: fullname, how: how)
24
+ end
25
+
26
+ # Remove the sigil that shows a thing was created by a moderator.
27
+ def undistinguish
28
+ distinguish(:no)
29
+ end
30
+
31
+ # Stop getting any moderator-related reports on the thing.
32
+ def ignore_reports
33
+ post('/api/ignore_reports', id: fullname)
34
+ end
35
+
36
+ # Start getting moderator-related reports on the thing again.
37
+ def unignore_reports
38
+ post('/api/unignore_reports', id: fullname)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that can be refreshed with the current data.
5
+ module Refreshable
6
+ # Refresh the thing.
7
+ def refresh!
8
+ body = get('/api/info', id: fullname).body[:data][:children][0]
9
+ deep_merge!(body[:data])
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that can be saved to a user's account.
5
+ module Saveable
6
+ # Save a link or comment (if gilded) to the user's account.
7
+ # @param [String] category A category to save to (if gilded).
8
+ def save(category = nil)
9
+ params = { id: fullname }
10
+ params[:category] = category if category
11
+ post('/api/save', params)
12
+ end
13
+
14
+ # Remove the link or comment from the user's saved links.
15
+ def unsave
16
+ post('/api/unsave', id: fullname)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ module Redd
2
+ module Objects
3
+ class Thing
4
+ # Things that can be voted upon.
5
+ module Votable
6
+ # Upvote the thing.
7
+ def upvote
8
+ vote(1)
9
+ end
10
+
11
+ # Downvote the thing.
12
+ def downvote
13
+ vote(-1)
14
+ end
15
+
16
+ # Remove your vote on the thing.
17
+ def clear_vote
18
+ vote(0)
19
+ end
20
+ alias unvote clear_vote
21
+
22
+ private
23
+
24
+ # Send a vote.
25
+ # @param [-1, 0, 1] direction The direction to vote in.
26
+ def vote(direction)
27
+ post('/api/vote', id: fullname, dir: direction)
28
+ self[:ups] += direction
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end