168 lines
5.4 KiB
Ruby
168 lines
5.4 KiB
Ruby
|
require 'twitter/rate_limit'
|
||
|
|
||
|
module Twitter
|
||
|
# Custom error class for rescuing from all Twitter errors
|
||
|
class Error < StandardError
|
||
|
attr_reader :code, :rate_limit
|
||
|
|
||
|
# If error code is missing see https://dev.twitter.com/docs/error-codes-responses
|
||
|
module Code
|
||
|
AUTHENTICATION_PROBLEM = 32
|
||
|
RESOURCE_NOT_FOUND = 34
|
||
|
SUSPENDED_ACCOUNT = 64
|
||
|
DEPRECATED_CALL = 68
|
||
|
RATE_LIMIT_EXCEEDED = 88
|
||
|
INVALID_OR_EXPIRED_TOKEN = 89
|
||
|
SSL_REQUIRED = 92
|
||
|
UNABLE_TO_VERIFY_CREDENTIALS = 99
|
||
|
OVER_CAPACITY = 130
|
||
|
INTERNAL_ERROR = 131
|
||
|
OAUTH_TIMESTAMP_OUT_OF_RANGE = 135
|
||
|
ALREADY_FAVORITED = 139
|
||
|
FOLLOW_ALREADY_REQUESTED = 160
|
||
|
FOLLOW_LIMIT_EXCEEDED = 161
|
||
|
PROTECTED_STATUS = 179
|
||
|
OVER_UPDATE_LIMIT = 185
|
||
|
DUPLICATE_STATUS = 187
|
||
|
BAD_AUTHENTICATION_DATA = 215
|
||
|
LOGIN_VERIFICATION_NEEDED = 231
|
||
|
ENDPOINT_RETIRED = 251
|
||
|
end
|
||
|
Codes = Code # rubocop:disable ConstantName
|
||
|
|
||
|
class << self
|
||
|
# Create a new error from an HTTP response
|
||
|
#
|
||
|
# @param response [Faraday::Response]
|
||
|
# @return [Twitter::Error]
|
||
|
def from_response(response)
|
||
|
message, code = parse_error(response.body)
|
||
|
new(message, response.response_headers, code)
|
||
|
end
|
||
|
|
||
|
# @return [Hash]
|
||
|
def errors
|
||
|
@errors ||= {
|
||
|
400 => Twitter::Error::BadRequest,
|
||
|
401 => Twitter::Error::Unauthorized,
|
||
|
403 => Twitter::Error::Forbidden,
|
||
|
404 => Twitter::Error::NotFound,
|
||
|
406 => Twitter::Error::NotAcceptable,
|
||
|
408 => Twitter::Error::RequestTimeout,
|
||
|
420 => Twitter::Error::EnhanceYourCalm,
|
||
|
422 => Twitter::Error::UnprocessableEntity,
|
||
|
429 => Twitter::Error::TooManyRequests,
|
||
|
500 => Twitter::Error::InternalServerError,
|
||
|
502 => Twitter::Error::BadGateway,
|
||
|
503 => Twitter::Error::ServiceUnavailable,
|
||
|
504 => Twitter::Error::GatewayTimeout,
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def forbidden_messages
|
||
|
@forbidden_messages ||= {
|
||
|
'Status is a duplicate.' => Twitter::Error::DuplicateStatus,
|
||
|
'You have already favorited this status.' => Twitter::Error::AlreadyFavorited,
|
||
|
'sharing is not permissible for this status (Share validations failed)' => Twitter::Error::AlreadyRetweeted,
|
||
|
}
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def parse_error(body)
|
||
|
if body.nil?
|
||
|
['', nil]
|
||
|
elsif body[:error]
|
||
|
[body[:error], nil]
|
||
|
elsif body[:errors]
|
||
|
extract_message_from_errors(body)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def extract_message_from_errors(body)
|
||
|
first = Array(body[:errors]).first
|
||
|
if first.is_a?(Hash)
|
||
|
[first[:message].chomp, first[:code]]
|
||
|
else
|
||
|
[first.chomp, nil]
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Initializes a new Error object
|
||
|
#
|
||
|
# @param message [Exception, String]
|
||
|
# @param rate_limit [Hash]
|
||
|
# @param code [Integer]
|
||
|
# @return [Twitter::Error]
|
||
|
def initialize(message = '', rate_limit = {}, code = nil)
|
||
|
super(message)
|
||
|
@rate_limit = Twitter::RateLimit.new(rate_limit)
|
||
|
@code = code
|
||
|
end
|
||
|
|
||
|
class ConfigurationError < ::ArgumentError; end
|
||
|
|
||
|
# Raised when a Tweet includes media that doesn't have a to_io method
|
||
|
class UnacceptableIO < StandardError
|
||
|
def initialize
|
||
|
super('The IO object for media must respond to to_io')
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Raised when Twitter returns a 4xx HTTP status code
|
||
|
class ClientError < self; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 400
|
||
|
class BadRequest < ClientError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 401
|
||
|
class Unauthorized < ClientError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 403
|
||
|
class Forbidden < ClientError; end
|
||
|
|
||
|
# Raised when a Tweet has already been favorited
|
||
|
class AlreadyFavorited < Forbidden; end
|
||
|
|
||
|
# Raised when a Tweet has already been retweeted
|
||
|
class AlreadyRetweeted < Forbidden; end
|
||
|
|
||
|
# Raised when a Tweet has already been posted
|
||
|
class DuplicateStatus < Forbidden; end
|
||
|
AlreadyPosted = DuplicateStatus # rubocop:disable ConstantName
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 404
|
||
|
class NotFound < ClientError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 406
|
||
|
class NotAcceptable < ClientError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 408
|
||
|
class RequestTimeout < ClientError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 422
|
||
|
class UnprocessableEntity < ClientError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 429
|
||
|
class TooManyRequests < ClientError; end
|
||
|
EnhanceYourCalm = TooManyRequests # rubocop:disable ConstantName
|
||
|
RateLimited = TooManyRequests # rubocop:disable ConstantName
|
||
|
|
||
|
# Raised when Twitter returns a 5xx HTTP status code
|
||
|
class ServerError < self; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 500
|
||
|
class InternalServerError < ServerError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 502
|
||
|
class BadGateway < ServerError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 503
|
||
|
class ServiceUnavailable < ServerError; end
|
||
|
|
||
|
# Raised when Twitter returns the HTTP status code 504
|
||
|
class GatewayTimeout < ServerError; end
|
||
|
end
|
||
|
end
|