1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
require 'twitter/rate_limit'
module Twitter
# Custom error class for rescuing from all Twitter errors
class Error < StandardError
# @return [Integer]
attr_reader :code
# @return [Twitter::RateLimit]
attr_reader :rate_limit
# Raised when Twitter returns a 4xx HTTP status code
ClientError = Class.new(self)
# Raised when Twitter returns the HTTP status code 400
BadRequest = Class.new(ClientError)
# Raised when Twitter returns the HTTP status code 401
Unauthorized = Class.new(ClientError)
# Raised when Twitter returns the HTTP status code 403
Forbidden = Class.new(ClientError)
# Raised when Twitter returns the HTTP status code 413
RequestEntityTooLarge = Class.new(ClientError)
# Raised when a Tweet has already been favorited
AlreadyFavorited = Class.new(Forbidden)
# Raised when a Tweet has already been retweeted
AlreadyRetweeted = Class.new(Forbidden)
# Raised when a Tweet has already been posted
DuplicateStatus = Class.new(Forbidden)
# Raised when Twitter returns the HTTP status code 404
NotFound = Class.new(ClientError)
# Raised when Twitter returns the HTTP status code 406
NotAcceptable = Class.new(ClientError)
# Raised when Twitter returns the HTTP status code 422
UnprocessableEntity = Class.new(ClientError)
# Raised when Twitter returns the HTTP status code 429
TooManyRequests = Class.new(ClientError)
# Raised when Twitter returns a 5xx HTTP status code
ServerError = Class.new(self)
# Raised when Twitter returns the HTTP status code 500
InternalServerError = Class.new(ServerError)
# Raised when Twitter returns the HTTP status code 502
BadGateway = Class.new(ServerError)
# Raised when Twitter returns the HTTP status code 503
ServiceUnavailable = Class.new(ServerError)
# Raised when Twitter returns the HTTP status code 504
GatewayTimeout = Class.new(ServerError)
# Raised when Twitter returns a media related error
MediaError = Class.new(self)
# Raised when Twitter returns an InvalidMedia error
InvalidMedia = Class.new(MediaError)
# Raised when Twitter returns a media InternalError error
MediaInternalError = Class.new(MediaError)
# Raised when Twitter returns an UnsupportedMedia error
UnsupportedMedia = Class.new(MediaError)
# Raised when an operation subject to timeout takes too long
TimeoutError = Class.new(self)
ERRORS = {
400 => Twitter::Error::BadRequest,
401 => Twitter::Error::Unauthorized,
403 => Twitter::Error::Forbidden,
404 => Twitter::Error::NotFound,
406 => Twitter::Error::NotAcceptable,
413 => Twitter::Error::RequestEntityTooLarge,
422 => Twitter::Error::UnprocessableEntity,
429 => Twitter::Error::TooManyRequests,
500 => Twitter::Error::InternalServerError,
502 => Twitter::Error::BadGateway,
503 => Twitter::Error::ServiceUnavailable,
504 => Twitter::Error::GatewayTimeout,
}.freeze
FORBIDDEN_MESSAGES = proc do |message|
case message
when /(?=.*status).*duplicate/i
# - "Status is a duplicate."
Twitter::Error::DuplicateStatus
when /already favorited/i
# - "You have already favorited this status."
Twitter::Error::AlreadyFavorited
when /already retweeted|Share validations failed/i
# - "You have already retweeted this Tweet." (Nov 2017-)
# - "You have already retweeted this tweet." (?-Nov 2017)
# - "sharing is not permissible for this status (Share validations failed)" (-? 2017)
Twitter::Error::AlreadyRetweeted
end
end
MEDIA_ERRORS = {
'InternalError' => Twitter::Error::MediaInternalError,
'InvalidMedia' => Twitter::Error::InvalidMedia,
'UnsupportedMedia' => Twitter::Error::UnsupportedMedia,
}.freeze
# If error code is missing see https://developer.twitter.com/en/docs/basics/response-codes
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
SPAM = 226
LOGIN_VERIFICATION_NEEDED = 231
ENDPOINT_RETIRED = 251
CANNOT_WRITE = 261
CANNOT_MUTE = 271
CANNOT_UNMUTE = 272
end
class << self
include Twitter::Utils
# Create a new error from an HTTP response
#
# @param body [String]
# @param headers [Hash]
# @return [Twitter::Error]
def from_response(body, headers)
message, code = parse_error(body)
new(message, headers, code)
end
# Create a new error from a media error hash
#
# @param error [Hash]
# @param headers [Hash]
# @return [Twitter::MediaError]
def from_processing_response(error, headers)
klass = MEDIA_ERRORS[error[:name]] || self
message = error[:message]
code = error[:code]
klass.new(message, headers, code)
end
private
def parse_error(body)
if body.nil? || body.empty?
['', 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
end
end
|