NeonRAW 0.1.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +5 -0
  5. data/.travis.yml +4 -0
  6. data/CHANGELOG.md +7 -0
  7. data/CONTRIBUTING.md +11 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.md +373 -0
  10. data/NeonRAW.gemspec +26 -0
  11. data/README.md +62 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/examples/crossposter.rb +78 -0
  16. data/examples/flairbot.rb +60 -0
  17. data/examples/publicmodlogger.rb +79 -0
  18. data/examples/settings.yaml +4 -0
  19. data/examples/userhistoryscraper.rb +108 -0
  20. data/examples/userhistorywiper.rb +10 -0
  21. data/lib/NeonRAW/clients/base/listing.rb +55 -0
  22. data/lib/NeonRAW/clients/base/objectbuilder.rb +173 -0
  23. data/lib/NeonRAW/clients/base/utilities.rb +46 -0
  24. data/lib/NeonRAW/clients/base.rb +109 -0
  25. data/lib/NeonRAW/clients/installed.rb +49 -0
  26. data/lib/NeonRAW/clients/script.rb +34 -0
  27. data/lib/NeonRAW/clients/web.rb +52 -0
  28. data/lib/NeonRAW/errors.rb +518 -0
  29. data/lib/NeonRAW/objects/access.rb +44 -0
  30. data/lib/NeonRAW/objects/all.rb +36 -0
  31. data/lib/NeonRAW/objects/comment.rb +128 -0
  32. data/lib/NeonRAW/objects/inboxcomment.rb +54 -0
  33. data/lib/NeonRAW/objects/listing.rb +12 -0
  34. data/lib/NeonRAW/objects/me.rb +268 -0
  35. data/lib/NeonRAW/objects/modlogaction.rb +59 -0
  36. data/lib/NeonRAW/objects/modloguser.rb +35 -0
  37. data/lib/NeonRAW/objects/morecomments.rb +33 -0
  38. data/lib/NeonRAW/objects/multireddit.rb +134 -0
  39. data/lib/NeonRAW/objects/privatemessage.rb +90 -0
  40. data/lib/NeonRAW/objects/rule.rb +41 -0
  41. data/lib/NeonRAW/objects/submission.rb +221 -0
  42. data/lib/NeonRAW/objects/subreddit/flair.rb +169 -0
  43. data/lib/NeonRAW/objects/subreddit/moderation.rb +200 -0
  44. data/lib/NeonRAW/objects/subreddit/utilities.rb +73 -0
  45. data/lib/NeonRAW/objects/subreddit/wiki.rb +31 -0
  46. data/lib/NeonRAW/objects/subreddit.rb +223 -0
  47. data/lib/NeonRAW/objects/thing/createable.rb +22 -0
  48. data/lib/NeonRAW/objects/thing/editable.rb +46 -0
  49. data/lib/NeonRAW/objects/thing/gildable.rb +29 -0
  50. data/lib/NeonRAW/objects/thing/inboxable.rb +26 -0
  51. data/lib/NeonRAW/objects/thing/moderateable.rb +98 -0
  52. data/lib/NeonRAW/objects/thing/refreshable.rb +21 -0
  53. data/lib/NeonRAW/objects/thing/repliable.rb +23 -0
  54. data/lib/NeonRAW/objects/thing/saveable.rb +26 -0
  55. data/lib/NeonRAW/objects/thing/votable.rb +69 -0
  56. data/lib/NeonRAW/objects/thing.rb +24 -0
  57. data/lib/NeonRAW/objects/trophy.rb +25 -0
  58. data/lib/NeonRAW/objects/user.rb +147 -0
  59. data/lib/NeonRAW/objects/wikipage.rb +176 -0
  60. data/lib/NeonRAW/objects/wikipagerevision.rb +45 -0
  61. data/lib/NeonRAW/version.rb +3 -0
  62. data/lib/NeonRAW.rb +43 -0
  63. metadata +161 -0
@@ -0,0 +1,49 @@
1
+ require 'cgi'
2
+ require_relative 'base'
3
+
4
+ module NeonRAW
5
+ module Clients
6
+ # The installed app client.
7
+ class Installed < Base
8
+ def initialize(client_id, redirect_uri, opts = {})
9
+ @client_id = client_id
10
+ @redirect_uri = redirect_uri
11
+ @user_agent = opts[:user_agent] ||
12
+ "Powered by NeonRAW v#{NeonRAW::VERSION}"
13
+ end
14
+
15
+ # Generates the authorization URL.
16
+ # @!method auth_url(state, scope = ['identity'], duration = 'temporary')
17
+ # @param state [String] A random string to check later.
18
+ # @param scope [Array<String>] The scope the app uses.
19
+ # @return [String] Returns the URL.
20
+ def auth_url(state, scope = ['identity'], duration = 'temporary')
21
+ query = {
22
+ response_type: 'token',
23
+ client_id: @client_id,
24
+ redirect_uri: @redirect_uri,
25
+ state: state,
26
+ scope: scope.join(','),
27
+ duration: duration
28
+ }
29
+ url = URI.join('https://www.reddit.com', '/api/v1/authorize')
30
+ url.query = URI.encode_www_form(query)
31
+ url.to_s
32
+ end
33
+
34
+ # Authorizes the client.
35
+ # @!method authorize!(fragment)
36
+ # @param fragment [String] The part of the URL after the #.
37
+ def authorize!(fragment)
38
+ data = CGI.parse(fragment)
39
+ access_data = {
40
+ access_token: data[:access_token].first,
41
+ token_type: data[:token_type].first,
42
+ expires_in: data[:expires_in].first,
43
+ scope: data[:scope].first
44
+ }
45
+ @access = Objects::Access.new(access_data)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'base'
2
+
3
+ module NeonRAW
4
+ module Clients
5
+ # The script app client.
6
+ class Script < Base
7
+ def initialize(username, password, client_id, secret, opts = {})
8
+ @username = username
9
+ @password = password
10
+ @client_id = client_id
11
+ @secret = secret
12
+ @redirect_uri = opts[:redirect_uri] || 'http://127.0.0.1:'
13
+ @user_agent = opts[:user_agent] ||
14
+ "Powered by NeonRAW v#{NeonRAW::VERSION}"
15
+ authorize!
16
+ end
17
+
18
+ # Authorizes the client for oAuth2 requests.
19
+ # @!method authorize!
20
+ # @!method refresh_access!
21
+ def authorize!
22
+ response = auth_connection(
23
+ '/api/v1/access_token', :post,
24
+ grant_type: 'password',
25
+ username: @username,
26
+ password: @password
27
+ )
28
+ data = JSON.parse(response.body, symbolize_names: true)
29
+ @access = Objects::Access.new(data)
30
+ end
31
+ alias refresh_access! authorize!
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ require 'uri'
2
+ require_relative 'base'
3
+
4
+ module NeonRAW
5
+ module Clients
6
+ # The Web app client.
7
+ class Web < Base
8
+ def initialize(client_id, secret, redirect_uri, opts = {})
9
+ @client_id = client_id
10
+ @secret = secret
11
+ @redirect_uri = redirect_uri
12
+ @user_agent = opts[:user_agent] ||
13
+ "Powered by NeonRAW v#{NeonRAW::VERSION}"
14
+ end
15
+
16
+ # Generates the authorization URL.
17
+ # @!method auth_url(state, scope = ['identity'], duration = 'temporary')
18
+ # @param state [String] A random string to check later.
19
+ # @param scope [Array<String>] The scopes your app uses.
20
+ # @param duration [String] The duration of the access token [temporary,
21
+ # permanent].
22
+ # @return [String] Returns the URL.
23
+ def auth_url(state, scope = ['identity'], duration = 'temporary')
24
+ query = {
25
+ response_type: 'code',
26
+ client_id: @client_id,
27
+ redirect_uri: @redirect_uri,
28
+ state: state,
29
+ scope: scope.join(','),
30
+ duration: duration
31
+ }
32
+ url = URI.join('https://www.reddit.com', '/api/v1/authorize')
33
+ url.query = URI.encode_www_form(query)
34
+ url.to_s
35
+ end
36
+
37
+ # Authorizes the client.
38
+ # @!method authorize!(code)
39
+ # @param code [String] The authorization code.
40
+ def authorize!(code)
41
+ response = auth_connection(
42
+ '/api/v1/access_token', :post,
43
+ grant_type: 'authorization_code',
44
+ code: code,
45
+ redirect_uri: @redirect_uri
46
+ )
47
+ data = JSON.parse(response.body, symbolize_names: true)
48
+ @access = Objects::Access.new(data)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,518 @@
1
+ # rubocop:disable all
2
+
3
+ module NeonRAW
4
+ # Methods and classes for handling errors.
5
+ module Errors
6
+ # Reads the HTTP status of the Typhoeus response and gives an exception to
7
+ # raise.
8
+ # @!method assign_errors(response)
9
+ # @param response [Typhoeus::Response] The response object.
10
+ # @return [StandardError, nil] Returns either the exception or nil if there
11
+ # is none.
12
+ def assign_errors(response)
13
+ code = response.code
14
+ body = response.body
15
+ case code
16
+ when 200
17
+ case body
18
+ when /access_denied/i then OAuth2AccessDenied
19
+ when /unsupported_response_type/i then InvalidResponseType
20
+ when /unsupported_grant_type/i then InvalidGrantType
21
+ when /invalid_scope/i then InvalidScope
22
+ when /invalid_request/i then InvalidRequest
23
+ when /invalid_grant/i then ExpiredCode
24
+ when /wrong_password/i then InvalidCredentials
25
+ when /bad_captcha/i then InvalidCaptcha
26
+ when /ratelimit/i then RateLimited
27
+ when /quota_filled/i then QuotaFilled
28
+ when /bad_css_name/i then InvalidClassName
29
+ when /too_old/i then Archived
30
+ when /too_much_flair_css/i then TooManyClassNames
31
+ when /user_required/i then AuthenticationRequired
32
+ when /bad_flair_target/i then BadFlairTarget
33
+ end
34
+ when 302 then UnexpectedRedirect
35
+ when 400 then BadRequest
36
+ when 401 then InvalidOAuth2Credentials
37
+ when 403
38
+ if /user_required/i =~ body
39
+ AuthenticationRequired
40
+ else
41
+ PermissionDenied
42
+ end
43
+ when 404 then NotFound
44
+ when 409 then Conflict
45
+ when 413 then RequestTooLarge
46
+ when 429 then RateLimited
47
+ when 500 then InternalServerError
48
+ when 502 then BadGateway
49
+ when 503 then ServiceUnavailable
50
+ when 504 then TimedOut
51
+ when 520 then CouldntReachServer
52
+ end
53
+ end
54
+
55
+ # Parses Reddit data for errors.
56
+ # @!method parse_errors(data)
57
+ # @param data [Array, Hash] The data.
58
+ def parse_errors(data)
59
+ # handles returns from toggleable methods
60
+ assign_data_errors([]) if data.empty?
61
+ if data.is_a?(Array) # handles returns from some flair methods
62
+ # handles multireddits
63
+ assign_data_errors([]) unless data[0].key?(:errors)
64
+ messages = []
65
+ errors = data[0][:errors]
66
+ errors.each { |_key, error| messages << error } unless errors.empty?
67
+ assign_data_errors(messages)
68
+ elsif data.key?(:json) # handles pretty much everything else
69
+ assign_data_errors([]) unless data[:json].key?(:errors)
70
+ if data[:json][:errors].is_a?(Array)
71
+ errors = data[:json][:errors][0] || []
72
+ assign_data_errors(errors)
73
+ else
74
+ errors = data[:json][:errors] || []
75
+ assign_data_errors(errors)
76
+ end
77
+ elsif data.key?(:errors) # handles image uploading
78
+ errors = data[:errors] || []
79
+ assign_data_errors(errors)
80
+ elsif data.key?(:jquery) # handles submitting submissions
81
+ errors = data[:jquery][-7][3]
82
+ assign_data_errors(errors)
83
+ end
84
+ end
85
+
86
+ # Checks data for any errors that wouldn't have otherwise thrown an
87
+ # exception.
88
+ # @!method assign_data_errors(errors)
89
+ # @param errors [Array<String>] The errors.
90
+ def assign_data_errors(errors)
91
+ return nil if errors.empty?
92
+ error = errors.first
93
+ case error
94
+ when /improperly formatted row/i then BadFlairRowFormat
95
+ when /no_subject/i then NoSubject
96
+ when /too_long/i then TooLong
97
+ when /no_text/i then NoText
98
+ when /subreddit_noexist/i then InvalidSubreddit
99
+ when /user_muted/i then UserMuted
100
+ when /no_sr_to_sr_message/i then InvalidSubreddit
101
+ when /user_blocked/i then UserBlocked
102
+ when /muted_from_subreddit/i then MutedFromSubreddit
103
+ when /subreddit_notallowed/i then PermissionDenied
104
+ when /no_selfs/i then NoSelfPosts
105
+ when /no_links/i then NoLinkPosts
106
+ when /url is required/i then NoUrl
107
+ when /already been submitted/i then AlreadySubmitted
108
+ when /no_invite_found/i then NoInviteFound
109
+ when /deleted_comment/i then DeletedComment
110
+ when /thread_locked/i then PermissionDenied
111
+ when /image_error/i then ImageError
112
+ when /subreddit_exists/i then SubredditExists
113
+ when /cant_create_sr/i then CantCreateSubreddit
114
+ when /invalid_option/i then InvalidOption
115
+ when /gold_required/i then GoldRequired
116
+ when /gold_only_sr_required/i then GoldOnlySrRequired
117
+ when /admin_required/i then PermissionDenied
118
+ when /bad_number/i then BadNumber
119
+ when /bad_sr_name/i then BadSubredditName
120
+ when /rows per call reached/i then TooManyFlairRows
121
+ when /unable to resolve user/i then CouldntResolveUser
122
+ when /sr_rule_exists/i then RuleExists
123
+ when /sr_rule_too_many/i then TooManyRules
124
+ end
125
+ end
126
+
127
+ # Manages the API ratelimit for requesting stuff from Reddit.
128
+ # @!method handle_ratelimit(headers)
129
+ # @param headers [Hash] The Typhoeus response headers.
130
+ def handle_ratelimit(headers)
131
+ requests_remaining = headers['X-Ratelimit-Remaining'].to_i
132
+ ratelimit_reset = headers['X-Ratelimit-Reset'].to_i
133
+ sleep(ratelimit_reset) unless requests_remaining > 0
134
+ end
135
+
136
+ # That URL has already been submitted.
137
+ class AlreadySubmitted < StandardError
138
+ def initialize(msg = 'That URL has already been submitted.')
139
+ super(msg)
140
+ end
141
+ end
142
+
143
+ # Thing is archived and can't be edited/replied to.
144
+ class Archived < StandardError
145
+ def initialize(msg = 'This thing is too old to edit/reply to.')
146
+ super(msg)
147
+ end
148
+ end
149
+
150
+ # Client needs to be authorized.
151
+ class AuthenticationRequired < StandardError
152
+ def initialize(msg = 'The client must be authorized to do that.')
153
+ super(msg)
154
+ end
155
+ end
156
+
157
+ # The flair row you sent was invalid.
158
+ # Should be: "Username,flairtext,CSSclass\nUsername,flairtext,CSSclass..."
159
+ class BadFlairRowFormat < StandardError
160
+ def initialize(msg = 'Improperly formatted row.')
161
+ super(msg)
162
+ end
163
+ end
164
+
165
+ # The thing you tried to flair was a bad target.
166
+ class BadFlairTarget < StandardError
167
+ def initialize(msg = 'Bad flair target.')
168
+ super(msg)
169
+ end
170
+ end
171
+
172
+ # Reddit's servers are shitting themselves.
173
+ class BadGateway < StandardError
174
+ def initialize(msg = "Reddit's server's are experiencing technical difficulties. Try again later.")
175
+ super(msg)
176
+ end
177
+ end
178
+
179
+ # The number value for a request parameter was incorrect.
180
+ class BadNumber < StandardError
181
+ def initialize(msg = 'The number passed to a request parameter was incorrect.')
182
+ super(msg)
183
+ end
184
+ end
185
+
186
+ # The request you sent to the API endpoint was bad.
187
+ class BadRequest < StandardError
188
+ def initialize(msg = 'The request you sent was incorrect. Please fix it.')
189
+ super(msg)
190
+ end
191
+ end
192
+
193
+ # The subreddit name was bad.
194
+ class BadSubredditName < StandardError
195
+ def initialize(msg = 'Bad subreddit name. Only [a-zA-Z0-9_] allowed.')
196
+ super(msg)
197
+ end
198
+ end
199
+
200
+ # Couldn't create the subreddit.
201
+ class CantCreateSubreddit < StandardError
202
+ def initialize(msg = "Couldn't create subreddit.")
203
+ super(msg)
204
+ end
205
+ end
206
+
207
+ # The multireddit you're trying to create already exists.
208
+ class Conflict < StandardError
209
+ def initialize(msg = "The multireddit you're trying to create already exists.")
210
+ super(msg)
211
+ end
212
+ end
213
+
214
+ # Reddit's servers are shitting themselves.
215
+ class CouldntReachServer < StandardError
216
+ def initialize(msg = "Reddit's servers are experiencing technical difficulties. Try again later.")
217
+ super(msg)
218
+ end
219
+ end
220
+
221
+ # Couldn't resolve the user provided.
222
+ class CouldntResolveUser < StandardError
223
+ def initialize(msg = "Couldn't resolve the user provided.")
224
+ super(msg)
225
+ end
226
+ end
227
+
228
+ # The comment you tried to reply to has been deleted.
229
+ class DeletedComment < StandardError
230
+ def initialize(msg = 'The comment you tried to reply to has been deleted.')
231
+ super(msg)
232
+ end
233
+ end
234
+
235
+ # You already received an access token using this code. They're only good
236
+ # for one use.
237
+ class ExpiredCode < StandardError
238
+ def initialize(msg = 'The code used to get the access token has expired.')
239
+ super(msg)
240
+ end
241
+ end
242
+
243
+ # Only gold-only subreddits can do that.
244
+ class GoldOnlySrRequired < StandardError
245
+ def initialize(msg = 'Only gold-only subreddits can do that.')
246
+ super(msg)
247
+ end
248
+ end
249
+
250
+ # You need gold to do that.
251
+ class GoldRequired < StandardError
252
+ def initialize(msg = 'You need gold to do that.')
253
+ super(msg)
254
+ end
255
+ end
256
+
257
+ # The image you tried to upload wasn't valid.
258
+ class ImageError < StandardError
259
+ def initialize(msg = "The image you tried to upload wasn't valid.")
260
+ super(msg)
261
+ end
262
+ end
263
+
264
+ # Reddit's servers are shitting themselves.
265
+ class InternalServerError < StandardError
266
+ def initialize(msg = "Reddit's servers are experiencing technical difficulties. Try again later.")
267
+ super(msg)
268
+ end
269
+ end
270
+
271
+ # You got the captcha wrong.
272
+ class InvalidCaptcha < StandardError
273
+ def initialize(msg = 'Invalid captcha.')
274
+ super(msg)
275
+ end
276
+ end
277
+
278
+ # You got the requested CSS class name wrong.
279
+ class InvalidClassName < StandardError
280
+ def initialize(msg = 'Invalid CSS class name.')
281
+ super(msg)
282
+ end
283
+ end
284
+
285
+ # Your username/password is wrong.
286
+ class InvalidCredentials < StandardError
287
+ def initialize(msg = 'Invalid username/password')
288
+ super(msg)
289
+ end
290
+ end
291
+
292
+ # Your grant_type is wrong.
293
+ class InvalidGrantType < StandardError
294
+ def initialize(msg = 'Invalid grant_type.')
295
+ super(msg)
296
+ end
297
+ end
298
+
299
+ # Your client_id/secret is wrong or your access token expired.
300
+ class InvalidOAuth2Credentials < StandardError
301
+ def initialize(msg = 'Invalid client_id/secret/access token.')
302
+ super(msg)
303
+ end
304
+ end
305
+
306
+ # Invalid option specified.
307
+ class InvalidOption < StandardError
308
+ def initialize(msg = 'One of the specified options is invalid.')
309
+ super(msg)
310
+ end
311
+ end
312
+
313
+ # The response_type parameter you sent was wrong. It should be the
314
+ # string "code".
315
+ class InvalidResponseType < StandardError
316
+ def initialize(msg = 'Invalid response_type. Should be "code".')
317
+ super(msg)
318
+ end
319
+ end
320
+
321
+ # The parameters sent to /api/v1/authorize were wrong.
322
+ class InvalidRequest < StandardError
323
+ def initialize(msg = 'Invalid /api/v1/authorize parameters.')
324
+ super(msg)
325
+ end
326
+ end
327
+
328
+ # You don't have the right scope to perform the request.
329
+ class InvalidScope < StandardError
330
+ def initialize(msg = "You don't have the right scope to do that.")
331
+ super(msg)
332
+ end
333
+ end
334
+
335
+ # Your from_subreddit parameter was wrong.
336
+ class InvalidSubreddit < StandardError
337
+ def initialize(msg = "The subreddit you specified is invalid.")
338
+ super(msg)
339
+ end
340
+ end
341
+
342
+ # You are muted from the subreddit.
343
+ class MutedFromSubreddit < StandardError
344
+ def initialize(msg = 'User is muted from the subreddit.')
345
+ super(msg)
346
+ end
347
+ end
348
+
349
+ # No moderator invite was found.
350
+ class NoInviteFound < StandardError
351
+ def initialize(msg = 'No moderator invite found.')
352
+ super(msg)
353
+ end
354
+ end
355
+
356
+ # The subreddit doesn't allow link posts.
357
+ class NoLinkPosts < StandardError
358
+ def initialize(msg = "The subreddit doesn't allow link posts.")
359
+ super(msg)
360
+ end
361
+ end
362
+
363
+ # The subreddit doesn't allow selfposts.
364
+ class NoSelfPosts < StandardError
365
+ def initialize(msg = "The subreddit doesn't allow selfposts.")
366
+ super(msg)
367
+ end
368
+ end
369
+
370
+ # You tried to send a private message with no subject.
371
+ class NoSubject < StandardError
372
+ def initialize(msg = 'No message subject. Please add a message subject.')
373
+ super(msg)
374
+ end
375
+ end
376
+
377
+ # You tried to send a message with no text.
378
+ class NoText < StandardError
379
+ def initialize(msg = 'No message text. Please add message text.')
380
+ super(msg)
381
+ end
382
+ end
383
+
384
+ # The thing you requested wasn't found. Could also mean that a user has
385
+ # been shadowbanned or a subreddit has been banned.
386
+ class NotFound < StandardError
387
+ def initialize(msg = "The thing you requested couldn't be found.")
388
+ super(msg)
389
+ end
390
+ end
391
+
392
+ # You didn't include an URL when submitting the submission.
393
+ class NoUrl < StandardError
394
+ def initialize(msg = 'No URL. Please add an URL.')
395
+ super(msg)
396
+ end
397
+ end
398
+
399
+ # The user chose not to grant your app access.
400
+ class OAuth2AccessDenied < StandardError
401
+ def initialize(msg = 'The user chose not to grant your app access.')
402
+ super(msg)
403
+ end
404
+ end
405
+
406
+ # You don't have adequate privileges to do that.
407
+ class PermissionDenied < StandardError
408
+ def initialize(msg = "You don't have permission to do that.")
409
+ super(msg)
410
+ end
411
+ end
412
+
413
+ # This is like being RateLimited only more oAuth2-focused I think.
414
+ class QuotaFilled < StandardError
415
+ def initialize(msg = 'Your quota is filled. Try again later.')
416
+ super(msg)
417
+ end
418
+ end
419
+
420
+ # Gotta wait before making another request.
421
+ class RateLimited < StandardError
422
+ def initialize(msg = "You're rate limited. Try again later.")
423
+ super(msg)
424
+ end
425
+ end
426
+
427
+ # There was an error with your request.
428
+ class RequestError < StandardError
429
+ def initialize(msg = 'There was an error with your request.')
430
+ super(msg)
431
+ end
432
+ end
433
+
434
+ # The request you sent was too large.
435
+ class RequestTooLarge < StandardError
436
+ def initialize(msg = 'The request you sent was too large.')
437
+ super(msg)
438
+ end
439
+ end
440
+
441
+ # This rule already exists.
442
+ class RuleExists < StandardError
443
+ def initialize(msg = 'This rule already exists.')
444
+ super(msg)
445
+ end
446
+ end
447
+
448
+ # Reddit's servers are shitting themselves/down for maintenance.
449
+ class ServiceUnavailable < StandardError
450
+ def initialize(msg = "Reddit's servers are currently unavailable. Try again later.")
451
+ super(msg)
452
+ end
453
+ end
454
+
455
+ # The subreddit you tried to create already exists.
456
+ class SubredditExists < StandardError
457
+ def initialize(msg = 'The subreddit you tried to create already exists.')
458
+ super(msg)
459
+ end
460
+ end
461
+
462
+ # The connection timed out.
463
+ class TimedOut < StandardError
464
+ def initialize(msg = 'Your connection timed out.')
465
+ super(msg)
466
+ end
467
+ end
468
+
469
+ # The text you tried to submit was too long.
470
+ class TooLong < StandardError
471
+ def initialize(msg = 'The text you tried to send was too long. 10,000 characters maximum.')
472
+ super(msg)
473
+ end
474
+ end
475
+
476
+ # You have too many flair classes already.
477
+ class TooManyClassNames < StandardError
478
+ def initialize(msg = 'Maxiumum number of flair classes reached.')
479
+ super(msg)
480
+ end
481
+ end
482
+
483
+ # You sent too many flair rows.
484
+ class TooManyFlairRows < StandardError
485
+ def initialize(msg = 'Too many flair rows. 100 maximum.')
486
+ super(msg)
487
+ end
488
+ end
489
+
490
+ # You already have the maximum amount of rules.
491
+ class TooManyRules < StandardError
492
+ def initialize(msg = 'You already have the maximum amount of rules.')
493
+ super(msg)
494
+ end
495
+ end
496
+
497
+ # Usually happens when the subreddit you requested doesn't exist.
498
+ class UnexpectedRedirect < StandardError
499
+ def initialize(msg = 'The subreddit you requested does not exist.')
500
+ super(msg)
501
+ end
502
+ end
503
+
504
+ # The user you tried to message is blocked.
505
+ class UserBlocked < StandardError
506
+ def initialize(msg = "Can't message blocked users.")
507
+ super(msg)
508
+ end
509
+ end
510
+
511
+ # The user you tried to message is muted from the subreddit.
512
+ class UserMuted < StandardError
513
+ def initialize(msg = 'User is muted.')
514
+ super(msg)
515
+ end
516
+ end
517
+ end
518
+ end
@@ -0,0 +1,44 @@
1
+ module NeonRAW
2
+ module Objects
3
+ # The access object
4
+ # @!attribute [r] access_token
5
+ # @return [String] Returns the access token used for oAuth2.
6
+ # @!attribute [r] token_type
7
+ # @return [String] Returns the type of the token (bearer)
8
+ # @!attribute [r] refresh_token
9
+ # @return [String, nil] Returns the refresh token or nil if there is none.
10
+ # @!attribute [r] scope
11
+ # @return [String] Returns the scope where the token is valid.
12
+ # @!attribute [r] expires_in
13
+ # @return [Time] Returns how long until the token expires.
14
+ # @!attribute [r] expires_at
15
+ # @return [Time] Returns when the token expires.
16
+ class Access
17
+ attr_reader :refresh_token
18
+ def initialize(data)
19
+ @refresh_token = nil
20
+ data.each do |key, value|
21
+ instance_variable_set(:"@#{key}", value)
22
+ next if key == :refresh_token
23
+ self.class.send(:attr_reader, key)
24
+ end
25
+ # I have it expire 10 seconds early to give a small buffer
26
+ # for requests to avoid getting those icky 401 errors.
27
+ @expires_at = Time.now + 3590
28
+ end
29
+
30
+ # @!method expired?
31
+ # @return [Boolean] Returns whether or not the token is expired.
32
+ def expired?
33
+ Time.now > @expires_at
34
+ end
35
+
36
+ # Refresh the access token.
37
+ # @!method refresh!(data)
38
+ # @param data [Hash] The new data.
39
+ def refresh!(data)
40
+ data.each { |key, value| instance_variable_set(:"@#{key}", value) }
41
+ end
42
+ end
43
+ end
44
+ end