spinels-redd 0.9.0

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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +7 -0
  3. data/.github/workflows/ci.yml +52 -0
  4. data/.gitignore +10 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +29 -0
  7. data/CONTRIBUTING.md +63 -0
  8. data/Gemfile +6 -0
  9. data/Guardfile +7 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +119 -0
  12. data/Rakefile +12 -0
  13. data/TODO.md +423 -0
  14. data/bin/console +127 -0
  15. data/bin/guard +2 -0
  16. data/bin/setup +8 -0
  17. data/docs/guides/.keep +0 -0
  18. data/docs/tutorials/creating-bots-with-redd.md +101 -0
  19. data/docs/tutorials/creating-webapps-with-redd.md +124 -0
  20. data/docs/tutorials/make-a-grammar-bot.md +5 -0
  21. data/docs/tutorials.md +7 -0
  22. data/lib/redd/api_client.rb +116 -0
  23. data/lib/redd/assist/delete_badly_scoring.rb +64 -0
  24. data/lib/redd/auth_strategies/auth_strategy.rb +68 -0
  25. data/lib/redd/auth_strategies/script.rb +35 -0
  26. data/lib/redd/auth_strategies/userless.rb +29 -0
  27. data/lib/redd/auth_strategies/web.rb +36 -0
  28. data/lib/redd/client.rb +91 -0
  29. data/lib/redd/errors.rb +65 -0
  30. data/lib/redd/middleware.rb +125 -0
  31. data/lib/redd/models/access.rb +54 -0
  32. data/lib/redd/models/comment.rb +229 -0
  33. data/lib/redd/models/front_page.rb +55 -0
  34. data/lib/redd/models/gildable.rb +13 -0
  35. data/lib/redd/models/inboxable.rb +33 -0
  36. data/lib/redd/models/listing.rb +52 -0
  37. data/lib/redd/models/live_thread.rb +133 -0
  38. data/lib/redd/models/live_update.rb +46 -0
  39. data/lib/redd/models/messageable.rb +20 -0
  40. data/lib/redd/models/mod_action.rb +59 -0
  41. data/lib/redd/models/model.rb +23 -0
  42. data/lib/redd/models/moderatable.rb +46 -0
  43. data/lib/redd/models/modmail.rb +61 -0
  44. data/lib/redd/models/modmail_conversation.rb +154 -0
  45. data/lib/redd/models/modmail_message.rb +35 -0
  46. data/lib/redd/models/more_comments.rb +96 -0
  47. data/lib/redd/models/multireddit.rb +104 -0
  48. data/lib/redd/models/paginated_listing.rb +124 -0
  49. data/lib/redd/models/postable.rb +83 -0
  50. data/lib/redd/models/private_message.rb +105 -0
  51. data/lib/redd/models/replyable.rb +16 -0
  52. data/lib/redd/models/reportable.rb +14 -0
  53. data/lib/redd/models/searchable.rb +35 -0
  54. data/lib/redd/models/self.rb +17 -0
  55. data/lib/redd/models/session.rb +198 -0
  56. data/lib/redd/models/submission.rb +405 -0
  57. data/lib/redd/models/subreddit.rb +670 -0
  58. data/lib/redd/models/trophy.rb +34 -0
  59. data/lib/redd/models/user.rb +239 -0
  60. data/lib/redd/models/wiki_page.rb +56 -0
  61. data/lib/redd/utilities/error_handler.rb +73 -0
  62. data/lib/redd/utilities/rate_limiter.rb +21 -0
  63. data/lib/redd/utilities/unmarshaller.rb +70 -0
  64. data/lib/redd/version.rb +5 -0
  65. data/lib/redd.rb +129 -0
  66. data/lib/spinels-redd.rb +3 -0
  67. data/logo.png +0 -0
  68. data/spinels-redd.gemspec +39 -0
  69. metadata +298 -0
@@ -0,0 +1,670 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'model'
4
+ require_relative 'messageable'
5
+ require_relative 'searchable'
6
+
7
+ module Redd
8
+ module Models
9
+ # A subreddit.
10
+ class Subreddit < Model # rubocop:disable Metrics/ClassLength
11
+ include Messageable
12
+ include Searchable
13
+
14
+ # A mapping from keys returned by #settings to keys required by #modify_settings
15
+ SETTINGS_MAP = {
16
+ subreddit_type: :type,
17
+ language: :lang,
18
+ content_options: :link_type,
19
+ default_set: :allow_top,
20
+ header_hover_text: :'header-title'
21
+ }
22
+
23
+ # @!group Listings
24
+
25
+ # Get the appropriate listing.
26
+ # @param sort [:hot, :new, :top, :controversial, :comments, :rising, :gilded] the type of
27
+ # listing
28
+ # @param options [Hash] a list of options to send with the request
29
+ # @option options [String] :after return results after the given fullname
30
+ # @option options [String] :before return results before the given fullname
31
+ # @option options [Integer, nil] :limit maximum number of items to return (nil for no limit)
32
+ # @option options [:hour, :day, :week, :month, :year, :all] :time the time period to consider
33
+ # when sorting
34
+ #
35
+ # @note The option :time only applies to the top and controversial sorts.
36
+ # @return [Listing<Submission, Comment>]
37
+ def listing(sort, **options)
38
+ options[:t] = options.delete(:time) if options.key?(:time)
39
+ PaginatedListing.new(client, **options) do |**req_options|
40
+ client.model(
41
+ :get, "/r/#{read_attribute(:display_name)}/#{sort}", options.merge(req_options)
42
+ )
43
+ end
44
+ end
45
+
46
+ # @!method hot(**options)
47
+ # @!method new(**options)
48
+ # @!method top(**options)
49
+ # @!method controversial(**options)
50
+ # @!method comments(**options)
51
+ # @!method rising(**options)
52
+ # @!method gilded(**options)
53
+ #
54
+ # @see #listing
55
+ %i[hot new top controversial comments rising gilded].each do |sort|
56
+ define_method(sort) { |**options| listing(sort, **options) }
57
+ end
58
+
59
+ # @!endgroup
60
+ # @!group Moderator Listings
61
+
62
+ # Get the appropriate moderator listing.
63
+ # @param type [:reports, :spam, :modqueue, :unmoderated, :edited] the type of listing
64
+ # @param params [Hash] a list of params to send with the request
65
+ # @option params [String] :after return results after the given fullname
66
+ # @option params [String] :before return results before the given fullname
67
+ # @option params [Integer] :count the number of items already seen in the listing
68
+ # @option params [1..100] :limit the maximum number of things to return
69
+ # @option params [:links, :comments] :only the type of objects required
70
+ #
71
+ # @return [Listing<Submission, Comment>]
72
+ def moderator_listing(type, **params)
73
+ client.model(:get, "/r/#{read_attribute(:display_name)}/about/#{type}", params)
74
+ end
75
+
76
+ # @!method reports(**params)
77
+ # @!method spam(**params)
78
+ # @!method modqueue(**params)
79
+ # @!method unmoderated(**params)
80
+ # @!method edited(**params)
81
+ #
82
+ # @see #moderator_listing
83
+ %i[reports spam modqueue unmoderated edited].each do |type|
84
+ define_method(type) { |**params| moderator_listing(type, **params) }
85
+ end
86
+
87
+ # @!endgroup
88
+ # @!group Relationship Listings
89
+
90
+ # Get the appropriate relationship listing.
91
+ # @param type [:banned, :muted, :wikibanned, :contributors, :wikicontributors, :moderators]
92
+ # the type of listing
93
+ # @param params [Hash] a list of params to send with the request
94
+ # @option params [String] :after return results after the given fullname
95
+ # @option params [String] :before return results before the given fullname
96
+ # @option params [Integer] :count the number of items already seen in the listing
97
+ # @option params [1..100] :limit the maximum number of things to return
98
+ # @option params [String] :user find a specific user
99
+ #
100
+ # @return [Array<Hash>]
101
+ def relationship_listing(type, **params)
102
+ # TODO: add methods to determine if a certain user was banned/muted/etc
103
+ # TODO: return User types?
104
+ user_list = client.get("/r/#{read_attribute(:display_name)}/about/#{type}", params).body
105
+ user_list[:data][:children]
106
+ end
107
+
108
+ # @!method banned(**params)
109
+ # @!method muted(**params)
110
+ # @!method wikibanned(**params)
111
+ # @!method contributors(**params)
112
+ # @!method wikicontributors(**params)
113
+ # @!method moderators(**params)
114
+ #
115
+ # @see #relationship_listing
116
+ %i[banned muted wikibanned contributors wikicontributors moderators].each do |type|
117
+ define_method(type) { |**params| relationship_listing(type, **params) }
118
+ end
119
+
120
+ # @!endgroup
121
+
122
+ # @return [Array<String>] the subreddit's wiki pages
123
+ def wiki_pages
124
+ client.get("/r/#{read_attribute(:display_name)}/wiki/pages").body[:data]
125
+ end
126
+
127
+ # Get a wiki page by its title.
128
+ # @param title [String] the page's title
129
+ # @return [WikiPage]
130
+ def wiki_page(title)
131
+ WikiPage.new(client, title: title, subreddit: self)
132
+ end
133
+
134
+ # Search a subreddit.
135
+ # @param query [String] the search query
136
+ # @param params [Hash] refer to {Searchable} to see search parameters
137
+ # @see Searchable#search
138
+ def search(query, **params)
139
+ restricted_params = { restrict_to: read_attribute(:display_name) }.merge(params)
140
+ super(query, restricted_params)
141
+ end
142
+
143
+ # Submit a link or a text post to the subreddit.
144
+ # @note If both text and url are provided, url takes precedence.
145
+ #
146
+ # @param title [String] the title of the submission
147
+ # @param text [String] the text of the self-post
148
+ # @param url [String] the URL of the link
149
+ # @param resubmit [Boolean] whether to post a link to the subreddit despite it having been
150
+ # posted there before (you monster)
151
+ # @param sendreplies [Boolean] whether to send the replies to your inbox
152
+ # @return [Submission] The returned object (url, id and name)
153
+ def submit(title, text: nil, url: nil, resubmit: false, sendreplies: true)
154
+ params = {
155
+ title: title, sr: read_attribute(:display_name),
156
+ resubmit: resubmit, sendreplies: sendreplies
157
+ }
158
+ params[:kind] = url ? 'link' : 'self'
159
+ params[:url] = url if url
160
+ params[:text] = text if text
161
+ Submission.new(client, client.post('/api/submit', params).body[:json][:data])
162
+ end
163
+
164
+ # Compose a message to the moderators of a subreddit.
165
+ #
166
+ # @param subject [String] the subject of the message
167
+ # @param text [String] the message text
168
+ # @param from [Subreddit, nil] the subreddit to send the message on behalf of
169
+ def send_message(subject:, text:, from: nil)
170
+ super(to: "/r/#{read_attribute(:display_name)}", subject: subject, text: text, from: from)
171
+ end
172
+
173
+ # Set the flair for a link or a user for this subreddit.
174
+ # @param thing [User, Submission] the object whose flair to edit
175
+ # @param text [String] a string no longer than 64 characters
176
+ # @param css_class [String] the css class to assign to the flair
177
+ def set_flair(thing, text, css_class: nil)
178
+ key = thing.is_a?(User) ? :name : :link
179
+ params = { :text => text, key => thing.name }
180
+ params[:css_class] = css_class if css_class
181
+ client.post("/r/#{read_attribute(:display_name)}/api/flair", params)
182
+ end
183
+
184
+ # Get a listing of all user flairs.
185
+ # @param params [Hash] a list of params to send with the request
186
+ # @option params [String] :after return results after the given fullname
187
+ # @option params [String] :before return results before the given fullname
188
+ # @option params [Integer] :count the number of items already seen in the listing
189
+ # @option params [String] :name prefer {#get_flair}
190
+ # @option params [:links, :comments] :only the type of objects required
191
+ #
192
+ # @return [Listing<Hash<Symbol, String>>]
193
+ def flair_listing(**params)
194
+ res = client.get("/r/#{read_attribute(:display_name)}/api/flairlist", params).body
195
+ Listing.new(client, children: res[:users], before: res[:prev], after: res[:next])
196
+ end
197
+
198
+ # Get the user's flair data.
199
+ # @param user [User] the user whose flair to fetch
200
+ # @return [Hash, nil]
201
+ def get_flair(user)
202
+ # We have to do this because reddit returns all flairs if given a nonexistent user
203
+ flair = flair_listing(name: user.name).first
204
+ return flair if flair && flair[:user].casecmp(user.name).zero?
205
+
206
+ nil
207
+ end
208
+
209
+ # Remove the flair from a user
210
+ # @param thing [User, String] a User from which to remove flair
211
+ def delete_flair(user)
212
+ name = user.is_a?(User) ? user.name : user
213
+ client.post("/r/#{read_attribute(:display_name)}/api/deleteflair", name: name)
214
+ end
215
+
216
+ # Set a Submission's or User's flair based on a flair template id.
217
+ # @param thing [User, Submission] an object to assign a template to
218
+ # @param template_id [String] the UUID of the flair template to assign
219
+ # @param text [String] optional text for the flair
220
+ def set_flair_template(thing, template_id, text: nil)
221
+ key = thing.is_a?(User) ? :name : :link
222
+ params = { key => thing.name, flair_template_id: template_id, text: text }
223
+ client.post("/r/#{read_attribute(:display_name)}/api/selectflair", params)
224
+ end
225
+
226
+ # Add the subreddit to the user's subscribed subreddits.
227
+ def subscribe(action: :sub, skip_initial_defaults: false)
228
+ client.post(
229
+ '/api/subscribe',
230
+ sr_name: read_attribute(:display_name),
231
+ action: action,
232
+ skip_initial_defaults: skip_initial_defaults
233
+ )
234
+ end
235
+
236
+ # Remove the subreddit from the user's subscribed subreddits.
237
+ def unsubscribe
238
+ subscribe(action: :unsub)
239
+ end
240
+
241
+ # Get the subreddit's CSS.
242
+ # @return [String, nil] the stylesheet or nil if no stylesheet exists
243
+ def stylesheet
244
+ url = client.get("/r/#{read_attribute(:display_name)}/stylesheet").headers['location']
245
+ HTTP.get(url).body.to_s
246
+ rescue Errors::NotFound
247
+ nil
248
+ end
249
+
250
+ # Edit the subreddit's stylesheet.
251
+ # @param text [String] the updated CSS
252
+ # @param reason [String] the reason for modifying the stylesheet
253
+ def update_stylesheet(text, reason: nil)
254
+ params = { op: 'save', stylesheet_contents: text }
255
+ params[:reason] = reason if reason
256
+ client.post("/r/#{read_attribute(:display_name)}/api/subreddit_stylesheet", params)
257
+ end
258
+
259
+ # @return [Hash] the subreddit's settings
260
+ def settings
261
+ client.get("/r/#{read_attribute(:display_name)}/about/edit").body[:data]
262
+ end
263
+
264
+ # Modify the subreddit's settings.
265
+ # @param params [Hash] the settings to change
266
+ # @see https://www.reddit.com/dev/api#POST_api_site_admin
267
+ def modify_settings(**params)
268
+ full_params = settings.merge(params)
269
+ full_params[:sr] = read_attribute(:name)
270
+ SETTINGS_MAP.each { |src, dest| full_params[dest] = full_params.delete(src) }
271
+ client.post('/api/site_admin', full_params)
272
+ end
273
+
274
+ # Get the moderation log.
275
+ # @param params [Hash] a list of params to send with the request
276
+ # @option params [String] :after return results after the given fullname
277
+ # @option params [String] :before return results before the given fullname
278
+ # @option params [Integer] :count the number of items already seen in the listing
279
+ # @option params [1..100] :limit the maximum number of things to return
280
+ # @option params [String] :type filter events to a specific type
281
+ #
282
+ # @return [Listing<ModAction>]
283
+ def mod_log(**params)
284
+ client.model(:get, "/r/#{read_attribute(:display_name)}/about/log", params)
285
+ end
286
+
287
+ # Invite a user to moderate this subreddit.
288
+ # @param user [User] the user to invite
289
+ # @param permissions [String] the permission string to invite the user with
290
+ def invite_moderator(user, permissions: '+all')
291
+ add_relationship(type: 'moderator_invite', name: user.name, permissions: permissions)
292
+ end
293
+
294
+ # Take back a moderator request.
295
+ # @param user [User] the requested user
296
+ def uninvite_moderator(user)
297
+ remove_relationship(type: 'moderator_invite', name: user.name)
298
+ end
299
+
300
+ # Accept an invite to become a moderator of this subreddit.
301
+ def accept_moderator_invite
302
+ client.post("/r/#{read_attribute(:display_name)}/api/accept_moderator_invite")
303
+ end
304
+
305
+ # Dethrone a moderator.
306
+ # @param user [User] the user to remove
307
+ def remove_moderator(user)
308
+ remove_relationship(type: 'moderator', name: user.name)
309
+ end
310
+
311
+ # Leave from being a moderator on a subreddit.
312
+ def leave_moderator
313
+ client.post('/api/leavemoderator', id: read_attribute(:name))
314
+ end
315
+
316
+ # Add a contributor to the subreddit.
317
+ # @param user [User] the user to add
318
+ def add_contributor(user)
319
+ add_relationship(type: 'contributor', name: user.name)
320
+ end
321
+
322
+ # Remove a contributor from the subreddit.
323
+ # @param user [User] the user to remove
324
+ def remove_contributor(user)
325
+ remove_relationship(type: 'contributor', name: user.name)
326
+ end
327
+
328
+ # Leave from being a contributor on a subreddit.
329
+ def leave_contributor
330
+ client.post('/api/leavecontributor', id: read_attribute(:name))
331
+ end
332
+
333
+ # Ban a user from a subreddit.
334
+ # @param user [User] the user to ban
335
+ # @param params [Hash] additional options to supply with the request
336
+ # @option params [String] :ban_reason the reason for the ban
337
+ # @option params [String] :ban_message a message sent to the banned user
338
+ # @option params [String] :note a note that only moderators can see
339
+ # @option params [Integer] :duration the number of days to ban the user (if temporary)
340
+ def ban(user, **params)
341
+ add_relationship(type: 'banned', name: user.name, **params)
342
+ end
343
+
344
+ # Remove a ban on a user.
345
+ # @param user [User] the user to unban
346
+ def unban(user)
347
+ remove_relationship(type: 'banned', name: user.name)
348
+ end
349
+
350
+ # Allow a user to contribute to the wiki.
351
+ # @param user [User] the user to add
352
+ def add_wiki_contributor(user)
353
+ add_relationship(type: 'wikicontributor', name: user.name)
354
+ end
355
+
356
+ # No longer allow a user to contribute to the wiki.
357
+ # @param user [User] the user to remove
358
+ def remove_wiki_contributor(user)
359
+ remove_relationship(type: 'wikicontributor', name: user.name)
360
+ end
361
+
362
+ # Ban a user from contributing to the wiki.
363
+ # @param user [User] the user to ban
364
+ # @param params [Hash] additional options to supply with the request
365
+ # @option params [String] :ban_reason the reason for the ban (not sure this matters)
366
+ # @option params [String] :note a note that only moderators can see
367
+ # @option params [Integer] :duration the number of days to ban the user (if temporary)
368
+ def ban_wiki_contributor(user, **params)
369
+ add_relationship(type: 'wikibanned', name: user.name, **params)
370
+ end
371
+
372
+ # No longer ban a user from contributing to the wiki.
373
+ # @param user [User] the user to unban
374
+ def unban_wiki_contributor(user)
375
+ remove_relationship(type: 'wikibanned', name: user.name)
376
+ end
377
+
378
+ # Upload a subreddit-specific image.
379
+ # @param file [String, IO] the image file to upload
380
+ # @param image_type ['jpg', 'png'] the image type
381
+ # @param upload_type ['img', 'header', 'icon', 'banner'] where to upload the image
382
+ # @param image_name [String] the name of the image (if upload_type is 'img')
383
+ # @return [String] the url of the uploaded file
384
+ def upload_image(file:, image_type:, upload_type:, image_name: nil)
385
+ file_data = HTTP::FormData::File.new(file)
386
+ params = { img_type: image_type, upload_type: upload_type, file: file_data }
387
+ params[:name] = image_name if upload_type.to_s == 'img'
388
+ client.post("/r/#{read_attribute(:display_name)}/api/upload_sr_img", params).body[:img_src]
389
+ end
390
+
391
+ # Delete a subreddit-specific image.
392
+ # @param upload_type ['img', 'header', 'icon', 'banner'] the image to delete
393
+ # @param image_name [String] the image name (if upload_type is 'img')
394
+ def delete_image(upload_type:, image_name: nil)
395
+ unless %w[img header icon banner].include?(upload_type)
396
+ raise ArgumentError, 'unknown upload_type'
397
+ end
398
+
399
+ params = {}
400
+ params[:name] = image_name if upload_type.to_s == 'img'
401
+ client.post("/r/#{read_attribute(:display_name)}/api/delete_sr_#{upload_type}", params)
402
+ end
403
+
404
+ # @!attribute [r] display_name
405
+ # @return [String] the subreddit's name
406
+ property :display_name, :required
407
+
408
+ # @!attribute [r] id
409
+ # @return [String] the subreddit's base36 id.
410
+ property :id
411
+
412
+ # @!attribute [r] name
413
+ # @return [String] the subreddit's t5_ fullname.
414
+ property :name
415
+
416
+ # @!attribute [r] title
417
+ # @return [String] the subreddit's page title text.
418
+ property :title
419
+
420
+ # @!attribute [r] user_is_contributor?
421
+ # @return [Boolean] whether the logged-in user is the subreddit's contributor
422
+ property :user_is_contributor?, from: :user_is_contributor
423
+
424
+ # @!attribute [r] banner_image
425
+ # @return [String] the url to the subreddit's banner image
426
+ property :banner_image, from: :banner_img
427
+
428
+ # @!attribute [r] banner_size
429
+ # @return [Array<Integer>] the banner dimensions
430
+ property :banner_size
431
+
432
+ # @!attribute [r] user_flair_text
433
+ # @return [String] the logged-in user's flair text
434
+ property :user_flair_text
435
+
436
+ # @!attribute [r] user_flair_css_class
437
+ # @return [String] the css class for the user's flair
438
+ property :user_flair_css_class
439
+
440
+ # @!attribute [r] user_is_banned
441
+ # @return [Boolean] whether the logged-in user is banned from this subreddit
442
+ property :user_is_banned?, from: :user_is_banned
443
+
444
+ # @!attribute [r] user_is_moderator?
445
+ # @return [Boolean] whether the logged-in user is a moderator of the subreddit
446
+ property :user_is_moderator?, from: :user_is_moderator
447
+
448
+ # @!attribute [r] user_is_muted?
449
+ # @return [Boolean] whether the logged-in user is muted from the subreddit
450
+ property :user_is_muted?, from: :user_is_muted
451
+
452
+ # @!attribute [r] user_is_subscriber
453
+ # @return [Boolean] whether the logged-in user is a subscriber to the subreddit
454
+ property :user_is_subscriber?, from: :user_is_subscriber
455
+
456
+ # @!attribute [r] wiki_enabled?
457
+ # @return [Boolean] whether the wiki is enabled for this subreddit
458
+ property :wiki_enabled?, from: :wiki_enabled
459
+
460
+ # @!attribute [r] show_media?
461
+ # @return [Boolean] whether media is shown
462
+ property :show_media?, from: :show_media
463
+
464
+ # @!attribute [r] description
465
+ # @return [String] the subreddit description
466
+ property :description
467
+
468
+ # @!attribute [r] description_html
469
+ # @return [String] the html-rendered version of the subreddit description
470
+ property :description_html
471
+
472
+ # @!attribute [r] submit_text
473
+ # @return [String] the submit text
474
+ property :submit_text
475
+
476
+ # @!attribute [r] submit_text_html
477
+ # @return [String] the submit text html
478
+ property :submit_text_html
479
+
480
+ # @!attribute [r] can_set_flair?
481
+ # @return [Boolean] whether the user can set the flair in the subreddit
482
+ property :can_set_flair?, from: :user_can_flair_in_sr
483
+
484
+ # @!attribute [r] header_img
485
+ # @return [String] the url to the header image
486
+ property :header_image, from: :header_img
487
+
488
+ # @!attribute [r] header_size
489
+ # @return [Array<Integer>] the dimensions of the header image
490
+ property :header_size
491
+
492
+ # @!attribute [r] collapse_deleted_comments?
493
+ # @return [Boolean] whether deleted comments are collapsed
494
+ property :collapse_deleted_comments?, from: :collapse_deleted_comments
495
+
496
+ # @!attribute [r] user_has_favorited?
497
+ # @return [Boolean] whether the user has favourited the subreddit
498
+ property :user_has_favorited?, from: :user_has_favorited
499
+
500
+ # @!attribute [r] public_description
501
+ # @return [String] the public description
502
+ property :public_description
503
+
504
+ # @!attribute [r] public_description_html
505
+ # @return [String] the html-rendered version of the public description
506
+ property :public_description_html
507
+
508
+ # @!attribute [r] over_18?
509
+ # @return [Boolean] whether the user is marked as over 18
510
+ property :over_18?, from: :over18
511
+
512
+ # @!attribute [r] spoilers_enabled?
513
+ # @return [Boolean] whether the subreddit has spoilers enabled
514
+ property :spoilers_enabled?, from: :spoilers_enabled
515
+
516
+ # @!attribute [r] icon_size
517
+ # @return [Array<Integer>] the subreddit icon size
518
+ property :icon_size
519
+
520
+ # @!attribute [r] audience_target
521
+ # @return [String] no clue what this means
522
+ property :audience_target
523
+
524
+ # @!attribute [r] suggested_comment_sort
525
+ # @return [String] the suggested comment sort
526
+ property :suggested_comment_sort
527
+
528
+ # @!attribute [r] active_user_count
529
+ # @return [Integer] the number of active users
530
+ property :active_user_count
531
+
532
+ # @!attribute [r] accounts_active
533
+ # @return [Integer] the number of active accounts
534
+ property :accounts_active
535
+
536
+ # @!attribute [r] subscribers
537
+ # @return [Integer] the subreddit's subscriber count
538
+ property :subscribers
539
+
540
+ # @!attribute [r] icon_image
541
+ # @return [String] the url to the icon image
542
+ property :icon_image, from: :icon_img
543
+
544
+ # @!attribute [r] header_title
545
+ # @return [String] the header's "title" attribute (i.e. mouseover text)
546
+ property :header_title
547
+
548
+ # @!attribute [r] display_name_prefixed
549
+ # @return [String] the display name, prefixed with a "r/".
550
+ # @deprecated not really deprecated, but prefer just using the display_name directly
551
+ property :display_name_prefixed, default: -> { "r/#{read_attribute(:display_name)}" }
552
+
553
+ # @!attribute [r] submit_link_label
554
+ # @return [String] the label text on the submit link button
555
+ property :submit_link_label
556
+
557
+ # @!attribute [r] submit_text_label
558
+ # @return [String] the label text on the submit text button
559
+ property :submit_text_label
560
+
561
+ # @!attribute [r] public_traffic
562
+ # @return [Boolean] whether the traffic page is public
563
+ property :public_traffic?, from: :public_traffic
564
+
565
+ # @!attribute [r] key_color
566
+ # @return [String] a hex color code, not sure what this does
567
+ property :key_color
568
+
569
+ # @!attribute [r] user_flair_visible?
570
+ # @return [Boolean] whether the user's flair is shown to others
571
+ property :user_flair_visible?, from: :user_sr_flair_enabled
572
+
573
+ # @!attribute [r] user_flair_enabled?
574
+ # @return [Boolean] whether the subreddit allows setting user flairs
575
+ property :user_flair_enabled?, from: :user_flair_enabled_in_sr
576
+
577
+ # @!attribute [r] language
578
+ # @return [String] the subreddit's language code
579
+ property :language, from: :lang
580
+
581
+ # @!attribute [r] enrolled_in_new_modmail?
582
+ # @return [Boolean] whether the subreddit is enrolled in the new modmail
583
+ property :enrolled_in_new_modmail?, from: :is_enrolled_in_new_modmail
584
+
585
+ # @!attribute [r] whitelist_status
586
+ # @return [String] not sure what this does, something to do with ads?
587
+ property :whitelist_status
588
+
589
+ # @!attribute [r] url
590
+ # @return [String] the subreddit's **relative** url (e.g. /r/Redd/)
591
+ property :url, default: -> { "/r/#{read_attribute(:display_name)}/" }
592
+
593
+ # @!attribute [r] quarantined?
594
+ # @return [Boolean] whether the subreddit is quarantined
595
+ property :quarantined?, from: :quarantine
596
+
597
+ # @!attribute [r] hide_ads?
598
+ # @return [Boolean] whether ads are hidden?
599
+ property :hide_ads?, from: :hide_ads
600
+
601
+ # @!attribute [r] created_at
602
+ # @return [Time] the time the subreddit was created
603
+ property :created_at, from: :created_utc, with: ->(t) { Time.at(t) }
604
+
605
+ # @!attribute [r] accounts_active_is_fuzzed
606
+ # @return [Boolean] whether active accounts is fuzzed
607
+ property :accounts_active_is_fuzzed?, from: :accounts_active_is_fuzzed
608
+
609
+ # @!attribute [r] advertiser_category
610
+ # @return [String] the advertiser category
611
+ property :advertiser_category
612
+
613
+ # @!attribute [r] subreddit_theme_enabled?
614
+ # @return [Boolean] whether the subreddit theme is enabled
615
+ property :subreddit_theme_enabled?, from: :user_sr_theme_enabled
616
+
617
+ # @!attribute [r] link_flair_enabled
618
+ # @return [Boolean] whether link flairs are enabled
619
+ property :link_flair_enabled?, from: :link_flair_enabled
620
+
621
+ # @!attribute [r] allow_images?
622
+ # @return [Boolean] whether images are allowed
623
+ property :allow_images?, from: :allow_images
624
+
625
+ # @!attribute [r] show_media_preview
626
+ # @return [Boolean] whether media previews are shown
627
+ property :show_media_preview?, from: :show_media_preview
628
+
629
+ # @!attribute [r] comment_score_hide_mins
630
+ # @return [Integer] the number of minutes the comment score is hidden
631
+ property :comment_score_hide_mins
632
+
633
+ # @!attribute [r] subreddit_type
634
+ # @return [String] whether it's a public, private, or gold-restricted subreddit
635
+ property :subreddit_type
636
+
637
+ # @!attribute [r] submission_type
638
+ # @return [String] the allowed submission type (?)
639
+ property :submission_type
640
+
641
+ private
642
+
643
+ def lazer_reload
644
+ fully_loaded!
645
+ exists_locally?(:name) ? load_from_fullname : load_from_display_name
646
+ end
647
+
648
+ # Return the attributes using the display_name (best option).
649
+ def load_from_display_name
650
+ client.get("/r/#{read_attribute(:display_name)}/about").body[:data]
651
+ end
652
+
653
+ # Load the attributes using the subreddit fullname (not so best option).
654
+ def load_from_fullname
655
+ response = client.get('/api/info', id: read_attribute(:name))
656
+ raise Errors::NotFound.new(response) if response.body[:data][:children].empty?
657
+
658
+ response.body[:data][:children][0][:data]
659
+ end
660
+
661
+ def add_relationship(**params)
662
+ client.post("/r/#{read_attribute(:display_name)}/api/friend", params)
663
+ end
664
+
665
+ def remove_relationship(**params)
666
+ client.post("/r/#{read_attribute(:display_name)}/api/unfriend", params)
667
+ end
668
+ end
669
+ end
670
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'model'
4
+
5
+ module Redd
6
+ module Models
7
+ # A user trophy.
8
+ class Trophy < Model
9
+ # @!attribute [r] icon_70px
10
+ # @return [String] the url for a 70x70 thumbnail icon
11
+ property :icon_70px, from: :icon_70
12
+
13
+ # @!attribute [r] icon_40px
14
+ # @return [String] the url for a 40x40 thumbnail icon
15
+ property :icon_40px, from: :icon_40
16
+
17
+ # @!attribute [r] name
18
+ # @return [String] the name of the trophy
19
+ property :name
20
+
21
+ # @!attribute [r] id
22
+ # @return [String] the trophy id
23
+ property :id
24
+
25
+ # @!attribute [r] award_id
26
+ # @return [String]
27
+ property :award_id
28
+
29
+ # @!attribute [r] description
30
+ # @return [String] the trophy description
31
+ property :description
32
+ end
33
+ end
34
+ end