NeonRAW 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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