redd 0.7.10 → 0.8.0.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -30
- data/.rspec +1 -1
- data/.rubocop.yml +16 -3
- data/.travis.yml +13 -7
- data/Gemfile +3 -1
- data/LICENSE.txt +21 -0
- data/README.md +40 -126
- data/Rakefile +10 -3
- data/TODO.md +11 -0
- data/bin/console +84 -0
- data/bin/setup +8 -0
- data/lib/redd.rb +84 -46
- data/lib/redd/api_client.rb +109 -0
- data/lib/redd/auth_strategies/auth_strategy.rb +60 -0
- data/lib/redd/auth_strategies/installed.rb +22 -0
- data/lib/redd/auth_strategies/script.rb +23 -0
- data/lib/redd/auth_strategies/userless.rb +17 -0
- data/lib/redd/auth_strategies/web.rb +29 -0
- data/lib/redd/client.rb +88 -0
- data/lib/redd/error.rb +19 -142
- data/lib/redd/models/access.rb +20 -0
- data/lib/redd/models/basic_model.rb +124 -0
- data/lib/redd/models/comment.rb +51 -0
- data/lib/redd/models/front_page.rb +71 -0
- data/lib/redd/models/inboxable.rb +23 -0
- data/lib/redd/models/lazy_model.rb +63 -0
- data/lib/redd/models/listing.rb +26 -0
- data/lib/redd/models/messageable.rb +20 -0
- data/lib/redd/models/moderatable.rb +41 -0
- data/lib/redd/models/more_comments.rb +10 -0
- data/lib/redd/models/multireddit.rb +32 -0
- data/lib/redd/models/postable.rb +70 -0
- data/lib/redd/models/private_message.rb +29 -0
- data/lib/redd/models/replyable.rb +16 -0
- data/lib/redd/models/session.rb +86 -0
- data/lib/redd/models/submission.rb +40 -0
- data/lib/redd/models/subreddit.rb +201 -0
- data/lib/redd/models/user.rb +72 -0
- data/lib/redd/models/wiki_page.rb +24 -0
- data/lib/redd/utilities/error_handler.rb +35 -0
- data/lib/redd/utilities/rate_limiter.rb +21 -0
- data/lib/redd/utilities/stream.rb +63 -0
- data/lib/redd/utilities/unmarshaller.rb +39 -0
- data/lib/redd/version.rb +4 -3
- data/logo.png +0 -0
- data/redd.gemspec +26 -22
- metadata +73 -99
- data/LICENSE.md +0 -22
- data/RedditKit.LICENSE.md +0 -9
- data/lib/redd/access.rb +0 -76
- data/lib/redd/clients/base.rb +0 -188
- data/lib/redd/clients/base/account.rb +0 -20
- data/lib/redd/clients/base/identity.rb +0 -22
- data/lib/redd/clients/base/none.rb +0 -27
- data/lib/redd/clients/base/privatemessages.rb +0 -33
- data/lib/redd/clients/base/read.rb +0 -113
- data/lib/redd/clients/base/stream.rb +0 -81
- data/lib/redd/clients/base/submit.rb +0 -19
- data/lib/redd/clients/base/utilities.rb +0 -104
- data/lib/redd/clients/base/wikiread.rb +0 -33
- data/lib/redd/clients/installed.rb +0 -57
- data/lib/redd/clients/script.rb +0 -41
- data/lib/redd/clients/userless.rb +0 -32
- data/lib/redd/clients/web.rb +0 -58
- data/lib/redd/objects/base.rb +0 -39
- data/lib/redd/objects/comment.rb +0 -22
- data/lib/redd/objects/labeled_multi.rb +0 -13
- data/lib/redd/objects/listing.rb +0 -29
- data/lib/redd/objects/more_comments.rb +0 -11
- data/lib/redd/objects/private_message.rb +0 -28
- data/lib/redd/objects/submission.rb +0 -139
- data/lib/redd/objects/subreddit.rb +0 -330
- data/lib/redd/objects/thing.rb +0 -26
- data/lib/redd/objects/thing/editable.rb +0 -22
- data/lib/redd/objects/thing/hideable.rb +0 -18
- data/lib/redd/objects/thing/inboxable.rb +0 -25
- data/lib/redd/objects/thing/messageable.rb +0 -34
- data/lib/redd/objects/thing/moderatable.rb +0 -43
- data/lib/redd/objects/thing/refreshable.rb +0 -14
- data/lib/redd/objects/thing/saveable.rb +0 -21
- data/lib/redd/objects/thing/votable.rb +0 -33
- data/lib/redd/objects/user.rb +0 -52
- data/lib/redd/objects/wiki_page.rb +0 -15
- data/lib/redd/rate_limit.rb +0 -88
- data/lib/redd/response/parse_json.rb +0 -18
- data/lib/redd/response/raise_error.rb +0 -16
- data/spec/redd/objects/base_spec.rb +0 -1
- data/spec/redd/response/raise_error_spec.rb +0 -11
- data/spec/redd_spec.rb +0 -5
- data/spec/spec_helper.rb +0 -71
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'client'
|
4
|
+
require_relative 'error'
|
5
|
+
require_relative 'utilities/error_handler'
|
6
|
+
require_relative 'utilities/rate_limiter'
|
7
|
+
require_relative 'utilities/unmarshaller'
|
8
|
+
|
9
|
+
module Redd
|
10
|
+
# The class for API clients.
|
11
|
+
class APIClient < Client
|
12
|
+
# The endpoint to make API requests to.
|
13
|
+
API_ENDPOINT = 'https://oauth.reddit.com'
|
14
|
+
|
15
|
+
# @return [APIClient] the access the client uses
|
16
|
+
attr_reader :access
|
17
|
+
|
18
|
+
# Create a new API client from an auth strategy.
|
19
|
+
# @param auth [AuthStrategies::AuthStrategy] the auth strategy to use
|
20
|
+
# @param endpoint [String] the API endpoint
|
21
|
+
# @param user_agent [String] the user agent to send
|
22
|
+
# @param limit_time [Integer] the minimum number of seconds between each request
|
23
|
+
# @param max_retries [Integer] number of times to retry requests that may be successful if
|
24
|
+
# retried
|
25
|
+
# @param auto_login [Boolean] (for script and userless) automatically authenticate if not done
|
26
|
+
# so already
|
27
|
+
# @param auto_refresh [Boolean] (for script and userless) automatically refresh access token if
|
28
|
+
# nearing expiration
|
29
|
+
def initialize(auth, endpoint: API_ENDPOINT, user_agent: USER_AGENT, limit_time: 1,
|
30
|
+
max_retries: 5, auto_login: true, auto_refresh: true)
|
31
|
+
super(endpoint: endpoint, user_agent: user_agent)
|
32
|
+
|
33
|
+
@auth = auth
|
34
|
+
@access = nil
|
35
|
+
@max_retries = max_retries
|
36
|
+
@failures = 0
|
37
|
+
@error_handler = Utilities::ErrorHandler.new
|
38
|
+
@rate_limiter = Utilities::RateLimiter.new(limit_time)
|
39
|
+
@unmarshaller = Utilities::Unmarshaller.new(self)
|
40
|
+
|
41
|
+
# FIXME: hard dependencies on Script and Userless types
|
42
|
+
can_auto = auth.is_a?(AuthStrategies::Script) || auth.is_a?(AuthStrategies::Userless)
|
43
|
+
@auto_login = can_auto && auto_login
|
44
|
+
@auto_refresh = can_auto && auto_refresh
|
45
|
+
end
|
46
|
+
|
47
|
+
# Authenticate the client using the provided auth.
|
48
|
+
def authenticate(*args)
|
49
|
+
@access = @auth.authenticate(*args)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Refresh the access currently in use.
|
53
|
+
def refresh(*args)
|
54
|
+
@access = @auth.refresh(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Revoke the current access and remove it from the client.
|
58
|
+
def revoke
|
59
|
+
@auth.revoke(@access)
|
60
|
+
@access = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def unmarshal(object)
|
64
|
+
@unmarshaller.unmarshal(object)
|
65
|
+
end
|
66
|
+
|
67
|
+
def model(verb, path, params = {})
|
68
|
+
# XXX: make unmarshal explicit in methods?
|
69
|
+
unmarshal(send(verb, path, params).body)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Makes sure a valid access is present, raising an error if nil
|
75
|
+
def ensure_access_is_valid
|
76
|
+
# Authenticate first if auto_login is enabled
|
77
|
+
authenticate if @access.nil? && @auto_login
|
78
|
+
# Refresh access if auto_refresh is enabled
|
79
|
+
refresh if @access.expired? && @auto_refresh
|
80
|
+
# Fuck it, panic
|
81
|
+
raise 'client access is nil, try calling #authenticate' if @access.nil?
|
82
|
+
end
|
83
|
+
|
84
|
+
def connection
|
85
|
+
super.auth("Bearer #{@access.access_token}")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Makes a request, ensuring not to break the rate limit by sleeping.
|
89
|
+
# @see Client#request
|
90
|
+
def request(verb, path, params: {}, form: {})
|
91
|
+
# Make sure @access is populated by a valid access
|
92
|
+
ensure_access_is_valid
|
93
|
+
# Setup base API params and make request
|
94
|
+
api_params = { api_type: 'json', raw_json: 1 }.merge(params)
|
95
|
+
response = @rate_limiter.after_limit { super(verb, path, params: api_params, form: form) }
|
96
|
+
# Check for errors in the returned response
|
97
|
+
response_error = @error_handler.check_error(response)
|
98
|
+
raise response_error unless response_error.nil?
|
99
|
+
# All done, return the response
|
100
|
+
@failures = 0
|
101
|
+
response
|
102
|
+
rescue Redd::ServerError, HTTP::TimeoutError => e
|
103
|
+
@failures += 1
|
104
|
+
raise e if @failures > @max_retries
|
105
|
+
warn "Redd got a #{e.class.name} error (#{e.message}), retrying..."
|
106
|
+
retry
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../client'
|
4
|
+
|
5
|
+
module Redd
|
6
|
+
module AuthStrategies
|
7
|
+
# The API client for authentication to reddit.
|
8
|
+
class AuthStrategy < Client
|
9
|
+
# The API to make authentication requests to.
|
10
|
+
AUTH_ENDPOINT = 'https://www.reddit.com'
|
11
|
+
|
12
|
+
# @param client_id [String] the client id of the reddit app
|
13
|
+
# @param secret [String] the app's secret string
|
14
|
+
# @param endpoint [String] the url to contact for authentication requests
|
15
|
+
# @param user_agent [String] the user agent to send with requests
|
16
|
+
def initialize(client_id:, secret:, endpoint: AUTH_ENDPOINT, user_agent: USER_AGENT)
|
17
|
+
super(endpoint: endpoint, user_agent: user_agent)
|
18
|
+
@client_id = client_id
|
19
|
+
@secret = secret
|
20
|
+
end
|
21
|
+
|
22
|
+
# @abstract Perform authentication and return the resulting access object
|
23
|
+
# @return [Access] the access token object
|
24
|
+
def authenticate(*)
|
25
|
+
raise 'abstract method: this strategy cannot authenticate with reddit'
|
26
|
+
end
|
27
|
+
|
28
|
+
# @abstract Refresh the authentication and return the refreshed access
|
29
|
+
# @return [Access] the new access
|
30
|
+
def refresh(*)
|
31
|
+
raise 'abstract method: this strategy cannot refresh access'
|
32
|
+
end
|
33
|
+
|
34
|
+
# Revoke the access token, making it invalid for future requests.
|
35
|
+
# @param access [Access] the access object to revoke
|
36
|
+
def revoke(access)
|
37
|
+
token =
|
38
|
+
if access.is_a?(String)
|
39
|
+
access
|
40
|
+
elsif access.respond_to?(:refresh_token)
|
41
|
+
access.refresh_token
|
42
|
+
else
|
43
|
+
access.access_token
|
44
|
+
end
|
45
|
+
post('/api/v1/revoke_token', token: token).body
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def connection
|
51
|
+
@connection ||= super.basic_auth(user: @client_id, pass: @secret)
|
52
|
+
end
|
53
|
+
|
54
|
+
def request_access(grant_type, options = {})
|
55
|
+
response = post('/api/v1/access_token', { grant_type: grant_type }.merge(options))
|
56
|
+
Models::Access.new(self, response.body)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'auth_strategy'
|
4
|
+
|
5
|
+
module Redd
|
6
|
+
module AuthStrategies
|
7
|
+
# For non-confidential apps. Different from the implicit grant.
|
8
|
+
class Installed < AuthStrategy
|
9
|
+
def initialize(client_id:, redirect_uri:, **kwargs)
|
10
|
+
super(client_id: client_id, secret: '', **kwargs)
|
11
|
+
@redirect_uri = redirect_uri
|
12
|
+
end
|
13
|
+
|
14
|
+
# Authenticate with a code using the "web" flow.
|
15
|
+
# @param code [String] the code returned by reddit
|
16
|
+
# @return [Access]
|
17
|
+
def authenticate(code)
|
18
|
+
request_access('authorization_code', code: code, redirect_uri: @redirect_uri)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'auth_strategy'
|
4
|
+
|
5
|
+
module Redd
|
6
|
+
module AuthStrategies
|
7
|
+
# A password-based authentication scheme. Requests all scopes.
|
8
|
+
class Script < AuthStrategy
|
9
|
+
def initialize(client_id:, secret:, username:, password:, **kwargs)
|
10
|
+
super(client_id: client_id, secret: secret, **kwargs)
|
11
|
+
@username = username
|
12
|
+
@password = password
|
13
|
+
end
|
14
|
+
|
15
|
+
# Perform authentication and return the resulting access object
|
16
|
+
# @return [Access] the access token object
|
17
|
+
def authenticate
|
18
|
+
request_access('password', username: @username, password: @password)
|
19
|
+
end
|
20
|
+
alias refresh authenticate
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'auth_strategy'
|
4
|
+
|
5
|
+
module Redd
|
6
|
+
module AuthStrategies
|
7
|
+
# A userless authentication scheme.
|
8
|
+
class Userless < AuthStrategy
|
9
|
+
# Perform authentication and return the resulting access object
|
10
|
+
# @return [Access] the access token object
|
11
|
+
def authenticate
|
12
|
+
request_access('client_credentials')
|
13
|
+
end
|
14
|
+
alias refresh authenticate
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'auth_strategy'
|
4
|
+
|
5
|
+
module Redd
|
6
|
+
module AuthStrategies
|
7
|
+
# A typical code-based authentication. I genuinely recommend this for bots.
|
8
|
+
# Only confidential web apps can be refreshed.
|
9
|
+
class Web < AuthStrategy
|
10
|
+
def initialize(client_id:, secret:, redirect_uri:, **kwargs)
|
11
|
+
super(client_id: client_id, secret: secret, **kwargs)
|
12
|
+
@redirect_uri = redirect_uri
|
13
|
+
end
|
14
|
+
|
15
|
+
# Authenticate with a code using the "web" flow.
|
16
|
+
# @param code [String] the code returned by reddit
|
17
|
+
# @return [Access]
|
18
|
+
def authenticate(code)
|
19
|
+
request_access('authorization_code', code: code, redirect_uri: @redirect_uri)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Refresh the authentication and return a new refreshed access
|
23
|
+
# @return [Access] the new access
|
24
|
+
def refresh(access)
|
25
|
+
request_access('refresh_token', refresh_token: must_have(access, :refresh_token))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/redd/client.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'http'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Redd
|
7
|
+
# The base class for JSON-based HTTP clients. Generic enough to be used for basically anything.
|
8
|
+
class Client
|
9
|
+
# The default User-Agent to use if none was provided.
|
10
|
+
USER_AGENT = "Ruby:Redd:v#{Redd::VERSION} (by unknown)"
|
11
|
+
|
12
|
+
# Holds a returned HTTP response.
|
13
|
+
Response = Struct.new(:code, :headers, :raw_body) do
|
14
|
+
def body
|
15
|
+
@body ||= JSON.parse(raw_body, symbolize_names: true)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create a new client.
|
20
|
+
# @param endpoint [String] the base endpoint to make all requests from
|
21
|
+
# @param user_agent [String] a user agent string
|
22
|
+
def initialize(endpoint:, user_agent: USER_AGENT)
|
23
|
+
@endpoint = endpoint
|
24
|
+
@user_agent = user_agent
|
25
|
+
end
|
26
|
+
|
27
|
+
# Make a GET request.
|
28
|
+
# @param path [String] the path relative to the endpoint
|
29
|
+
# @param options [Hash] the parameters to supply
|
30
|
+
# @return [Response] the response
|
31
|
+
def get(path, options = {})
|
32
|
+
request(:get, path, params: options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Make a POST request.
|
36
|
+
# @param path [String] the path relative to the endpoint
|
37
|
+
# @param options [Hash] the parameters to supply
|
38
|
+
# @return [Response] the response
|
39
|
+
def post(path, options = {})
|
40
|
+
request(:post, path, form: options)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Make a PUT request.
|
44
|
+
# @param path [String] the path relative to the endpoint
|
45
|
+
# @param options [Hash] the parameters to supply
|
46
|
+
# @return [Response] the response
|
47
|
+
def put(path, options = {})
|
48
|
+
request(:put, path, form: options)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Make a PATCH request.
|
52
|
+
# @param path [String] the path relative to the endpoint
|
53
|
+
# @param options [Hash] the parameters to supply
|
54
|
+
# @return [Response] the response
|
55
|
+
def patch(path, options = {})
|
56
|
+
request(:patch, path, form: options)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Make a DELETE request.
|
60
|
+
# @param path [String] the path relative to the endpoint
|
61
|
+
# @param options [Hash] the parameters to supply
|
62
|
+
# @return [Response] the response
|
63
|
+
def delete(path, options = {})
|
64
|
+
request(:delete, path, form: options)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# @return [HTTP::Connection] the base connection object
|
70
|
+
def connection
|
71
|
+
# TODO: Make timeouts configurable
|
72
|
+
@connection ||= HTTP.persistent(@endpoint)
|
73
|
+
.headers('User-Agent' => @user_agent)
|
74
|
+
.timeout(:per_operation, write: 5, connect: 5, read: 5)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Make an HTTP request.
|
78
|
+
# @param verb [:get, :post, :put, :patch, :delete] the HTTP verb to use
|
79
|
+
# @param path [String] the path relative to the endpoint
|
80
|
+
# @param params [Hash] the parameters to supply with the url
|
81
|
+
# @param form [Hash] the parameters to supply in the body
|
82
|
+
# @return [Response] the response
|
83
|
+
def request(verb, path, params: {}, form: {})
|
84
|
+
response = connection.request(verb, path, params: params, form: form)
|
85
|
+
Response.new(response.status.code, response.headers, response.body.to_s)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/redd/error.rb
CHANGED
@@ -1,151 +1,28 @@
|
|
1
|
-
|
2
|
-
# An error from reddit
|
3
|
-
# TODO: Move Error to an Errors module in next minor version?
|
4
|
-
class Error < StandardError
|
5
|
-
attr_reader :code
|
6
|
-
attr_reader :headers
|
7
|
-
attr_reader :body
|
8
|
-
|
9
|
-
def initialize(env)
|
10
|
-
@code = env[:status]
|
11
|
-
@headers = env[:response_headers]
|
12
|
-
@body = env[:body]
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.from_response(env) # rubocop:disable all
|
16
|
-
status = env[:status]
|
17
|
-
body = parse_error(env[:body]).to_s
|
18
|
-
case status
|
19
|
-
when 200
|
20
|
-
case body
|
21
|
-
when /access_denied/i then OAuth2AccessDenied
|
22
|
-
when /unsupported_response_type/i then InvalidResponseType
|
23
|
-
when /unsupported_grant_type/i then InvalidGrantType
|
24
|
-
when /invalid_scope/i then InvalidScope
|
25
|
-
when /invalid_request/i then InvalidRequest
|
26
|
-
when /no_text/i then NoTokenGiven
|
27
|
-
when /invalid_grant/i then ExpiredCode
|
28
|
-
when /wrong_password/i then InvalidCredentials
|
29
|
-
when /bad_captcha/i then InvalidCaptcha
|
30
|
-
when /ratelimit/i then RateLimited
|
31
|
-
when /quota_filled/i then QuotaFilled
|
32
|
-
when /bad_css_name/i then InvalidClassName
|
33
|
-
when /too_old/i then Archived
|
34
|
-
when /too_much_flair_css/i then TooManyClassNames
|
35
|
-
when /user_required/i then AuthenticationRequired
|
36
|
-
end
|
37
|
-
when 400 then BadRequest
|
38
|
-
when 401 then InvalidOAuth2Credentials
|
39
|
-
when 403
|
40
|
-
if /user_required/i =~ body
|
41
|
-
AuthenticationRequired
|
42
|
-
else
|
43
|
-
PermissionDenied
|
44
|
-
end
|
45
|
-
when 404 then NotFound
|
46
|
-
when 409 then Conflict
|
47
|
-
when 500 then InternalServerError
|
48
|
-
when 502 then BadGateway
|
49
|
-
when 503 then ServiceUnavailable
|
50
|
-
when 504 then TimedOut
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.parse_error(body) # rubocop:disable all
|
55
|
-
return body unless body.is_a?(Hash)
|
56
|
-
|
57
|
-
if body.key?(:json) && body[:json].key?(:errors)
|
58
|
-
body[:json][:errors].first
|
59
|
-
elsif body.key?(:jquery)
|
60
|
-
body[:jquery]
|
61
|
-
elsif body.key?(:error)
|
62
|
-
body[:error]
|
63
|
-
elsif body.key?(:code) && body[:code] == "NO_TEXT"
|
64
|
-
"NO_TEXT"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# This item has been archived and can no longer be edited.
|
69
|
-
Archived = Class.new(Error)
|
70
|
-
|
71
|
-
AuthenticationRequired = Class.new(Error)
|
72
|
-
|
73
|
-
# Bad Gateway. Either a network or a reddit error. Either way, try again.
|
74
|
-
BadGateway = Class.new(Error)
|
75
|
-
|
76
|
-
BadRequest = Class.new(Error)
|
77
|
-
|
78
|
-
Conflict = Class.new(Error)
|
79
|
-
|
80
|
-
# You already received an access token using this code. The user should
|
81
|
-
# grant you access again to get a new code.
|
82
|
-
ExpiredCode = Class.new(Error)
|
83
|
-
|
84
|
-
# There is an issue on reddit's end. Try again.
|
85
|
-
InternalServerError = Class.new(Error)
|
86
|
-
|
87
|
-
InvalidCaptcha = Class.new(Error)
|
88
|
-
|
89
|
-
InvalidClassName = Class.new(Error)
|
1
|
+
# frozen_string_literal: true
|
90
2
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
InvalidMultiredditName = Class.new(Error)
|
97
|
-
|
98
|
-
# Your client id or your secret is wrong.
|
99
|
-
InvalidOAuth2Credentials = Class.new(Error)
|
100
|
-
|
101
|
-
InvalidResponseType = Class.new(Error)
|
102
|
-
|
103
|
-
InvalidRequest = Class.new(Error)
|
104
|
-
|
105
|
-
# You don't have the proper scope to perform this request.
|
106
|
-
InvalidScope = Class.new(Error)
|
107
|
-
|
108
|
-
# Looks like we didn't get a JSON response. Raise this error.
|
109
|
-
JSONError = Class.new(Error)
|
110
|
-
|
111
|
-
# Four, oh four! The thing you're looking for wasn't found.
|
112
|
-
NotFound = Class.new(Error)
|
113
|
-
|
114
|
-
# No access token was given.
|
115
|
-
NoTokenGiven = Class.new(Error)
|
116
|
-
|
117
|
-
OAuth2AccessDenied = Class.new(Error)
|
118
|
-
|
119
|
-
PermissionDenied = Class.new(Error)
|
120
|
-
|
121
|
-
# Raised when the client needs to wait before making another request
|
122
|
-
class RateLimited < Error
|
123
|
-
# @!attribute [r] time
|
124
|
-
# @return [Integer] the seconds to wait before making another request.
|
125
|
-
attr_reader :time
|
3
|
+
module Redd
|
4
|
+
# Represents an error from reddit returned in a response.
|
5
|
+
class ResponseError < StandardError
|
6
|
+
attr_accessor :response
|
126
7
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
8
|
+
def initialize(response)
|
9
|
+
super(response.raw_body.length <= 80 ? response.raw_body : "#{response.raw_body[0..80]}...")
|
10
|
+
@response = response
|
131
11
|
end
|
12
|
+
end
|
132
13
|
|
133
|
-
|
134
|
-
|
135
|
-
def initialize(env)
|
136
|
-
super
|
137
|
-
@time = 3600
|
138
|
-
end
|
139
|
-
end
|
14
|
+
# An error with Redd, probably (let me know!)
|
15
|
+
class BadRequest < ResponseError; end
|
140
16
|
|
141
|
-
|
17
|
+
# You don't have the correct scope to do this.
|
18
|
+
class InsufficientScope < ResponseError; end
|
142
19
|
|
143
|
-
|
144
|
-
|
20
|
+
# The access object supplied was invalid.
|
21
|
+
class InvalidAccess < ResponseError; end
|
145
22
|
|
146
|
-
|
147
|
-
|
23
|
+
# Returned when reddit raises a 404 error.
|
24
|
+
class NotFound < ResponseError; end
|
148
25
|
|
149
|
-
|
150
|
-
end
|
26
|
+
# An unknown error on reddit's end. Usually fixed with a retry.
|
27
|
+
class ServerError < ResponseError; end
|
151
28
|
end
|