twitter 6.1.0 → 6.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +22 -15
- data/README.md +69 -394
- data/lib/twitter/base.rb +2 -16
- data/lib/twitter/basic_user.rb +0 -3
- data/lib/twitter/client.rb +5 -19
- data/lib/twitter/configuration.rb +3 -2
- data/lib/twitter/creatable.rb +1 -1
- data/lib/twitter/cursor.rb +0 -1
- data/lib/twitter/entity/uri.rb +0 -4
- data/lib/twitter/error.rb +81 -96
- data/lib/twitter/geo_results.rb +0 -1
- data/lib/twitter/headers.rb +11 -10
- data/lib/twitter/media/photo.rb +4 -0
- data/lib/twitter/media/video.rb +4 -1
- data/lib/twitter/media/video_info.rb +1 -1
- data/lib/twitter/null_object.rb +2 -2
- data/lib/twitter/profile.rb +3 -2
- data/lib/twitter/rate_limit.rb +1 -1
- data/lib/twitter/rest/api.rb +0 -2
- data/lib/twitter/rest/client.rb +0 -78
- data/lib/twitter/rest/direct_messages.rb +3 -3
- data/lib/twitter/rest/favorites.rb +25 -4
- data/lib/twitter/rest/friends_and_followers.rb +1 -3
- data/lib/twitter/rest/lists.rb +18 -29
- data/lib/twitter/rest/oauth.rb +15 -15
- data/lib/twitter/rest/request.rb +105 -16
- data/lib/twitter/rest/saved_searches.rb +0 -2
- data/lib/twitter/rest/search.rb +2 -0
- data/lib/twitter/rest/trends.rb +1 -0
- data/lib/twitter/rest/tweets.rb +80 -15
- data/lib/twitter/rest/undocumented.rb +3 -4
- data/lib/twitter/rest/users.rb +20 -34
- data/lib/twitter/rest/utils.rb +13 -20
- data/lib/twitter/search_results.rb +1 -2
- data/lib/twitter/streaming/client.rb +17 -23
- data/lib/twitter/streaming/connection.rb +23 -10
- data/lib/twitter/streaming/deleted_tweet.rb +0 -1
- data/lib/twitter/streaming/event.rb +6 -6
- data/lib/twitter/streaming/message_parser.rb +1 -1
- data/lib/twitter/streaming/response.rb +2 -2
- data/lib/twitter/trend_results.rb +1 -2
- data/lib/twitter/tweet.rb +1 -5
- data/lib/twitter/user.rb +0 -2
- data/lib/twitter/utils.rb +1 -16
- data/lib/twitter/version.rb +1 -1
- data/twitter.gemspec +13 -13
- metadata +35 -27
- data/lib/twitter/rest/media.rb +0 -30
- data/lib/twitter/rest/request/multipart_with_file.rb +0 -47
- data/lib/twitter/rest/response/parse_error_json.rb +0 -13
- data/lib/twitter/rest/response/parse_json.rb +0 -31
- data/lib/twitter/rest/response/raise_error.rb +0 -32
- data/lib/twitter/token.rb +0 -20
data/lib/twitter/base.rb
CHANGED
@@ -13,7 +13,6 @@ module Twitter
|
|
13
13
|
attr_reader :attrs
|
14
14
|
alias to_h attrs
|
15
15
|
alias to_hash to_h
|
16
|
-
deprecate_alias :to_hsh, :to_hash
|
17
16
|
|
18
17
|
class << self
|
19
18
|
# Define methods that retrieve the value from attributes
|
@@ -29,7 +28,6 @@ module Twitter
|
|
29
28
|
def predicate_attr_reader(*attrs)
|
30
29
|
attrs.each do |attr|
|
31
30
|
define_predicate_method(attr)
|
32
|
-
deprecate_attribute_method(attr)
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -87,25 +85,13 @@ module Twitter
|
|
87
85
|
define_method(key1) do
|
88
86
|
if attr_falsey_or_empty?(key1)
|
89
87
|
NullObject.new
|
90
|
-
elsif klass.nil?
|
91
|
-
@attrs[key1]
|
92
88
|
else
|
93
|
-
attrs
|
94
|
-
Twitter.const_get(klass).new(attrs)
|
89
|
+
klass.nil? ? @attrs[key1] : Twitter.const_get(klass).new(attrs_for_object(key1, key2))
|
95
90
|
end
|
96
91
|
end
|
97
92
|
memoize(key1)
|
98
93
|
end
|
99
94
|
|
100
|
-
# @param key [Symbol]
|
101
|
-
def deprecate_attribute_method(key)
|
102
|
-
define_method(key) do
|
103
|
-
warn "#{Kernel.caller.first}: [DEPRECATION] ##{key} is deprecated. Use ##{key}? instead."
|
104
|
-
@attrs[key]
|
105
|
-
end
|
106
|
-
memoize(key)
|
107
|
-
end
|
108
|
-
|
109
95
|
# Dynamically define a predicate method for an attribute
|
110
96
|
#
|
111
97
|
# @param key1 [Symbol]
|
@@ -139,7 +125,7 @@ module Twitter
|
|
139
125
|
private
|
140
126
|
|
141
127
|
def attr_falsey_or_empty?(key)
|
142
|
-
|
128
|
+
!@attrs[key] || @attrs[key].respond_to?(:empty?) && @attrs[key].empty?
|
143
129
|
end
|
144
130
|
|
145
131
|
def attrs_for_object(key1, key2 = nil)
|
data/lib/twitter/basic_user.rb
CHANGED
@@ -5,9 +5,6 @@ module Twitter
|
|
5
5
|
class BasicUser < Twitter::Identity
|
6
6
|
# @return [String]
|
7
7
|
attr_reader :screen_name
|
8
|
-
deprecate_alias :handle, :screen_name
|
9
|
-
deprecate_alias :username, :screen_name
|
10
|
-
deprecate_alias :user_name, :screen_name
|
11
8
|
predicate_attr_reader :following
|
12
9
|
end
|
13
10
|
end
|
data/lib/twitter/client.rb
CHANGED
@@ -5,12 +5,8 @@ require 'twitter/version'
|
|
5
5
|
module Twitter
|
6
6
|
class Client
|
7
7
|
include Twitter::Utils
|
8
|
-
attr_accessor :access_token, :access_token_secret, :consumer_key, :consumer_secret, :proxy
|
8
|
+
attr_accessor :access_token, :access_token_secret, :consumer_key, :consumer_secret, :proxy, :timeouts
|
9
9
|
attr_writer :user_agent
|
10
|
-
deprecate_alias :oauth_token, :access_token
|
11
|
-
deprecate_alias :oauth_token=, :access_token=
|
12
|
-
deprecate_alias :oauth_token_secret, :access_token_secret
|
13
|
-
deprecate_alias :oauth_token_secret=, :access_token_secret=
|
14
10
|
|
15
11
|
# Initializes a new Client object
|
16
12
|
#
|
@@ -21,12 +17,11 @@ module Twitter
|
|
21
17
|
instance_variable_set("@#{key}", value)
|
22
18
|
end
|
23
19
|
yield(self) if block_given?
|
24
|
-
validate_credentials!
|
25
20
|
end
|
26
21
|
|
27
22
|
# @return [Boolean]
|
28
23
|
def user_token?
|
29
|
-
|
24
|
+
!(blank?(access_token) || blank?(access_token_secret))
|
30
25
|
end
|
31
26
|
|
32
27
|
# @return [String]
|
@@ -41,27 +36,18 @@ module Twitter
|
|
41
36
|
consumer_secret: consumer_secret,
|
42
37
|
token: access_token,
|
43
38
|
token_secret: access_token_secret,
|
44
|
-
ignore_extra_keys: true,
|
45
39
|
}
|
46
40
|
end
|
47
41
|
|
48
42
|
# @return [Boolean]
|
49
43
|
def credentials?
|
50
|
-
credentials.values.
|
44
|
+
credentials.values.none? { |v| blank?(v) }
|
51
45
|
end
|
52
46
|
|
53
47
|
private
|
54
48
|
|
55
|
-
|
56
|
-
|
57
|
-
#
|
58
|
-
# @raise [Twitter::Error::ConfigurationError] Error is raised when
|
59
|
-
# supplied twitter credentials are not a String or Boolean.
|
60
|
-
def validate_credentials!
|
61
|
-
credentials.each do |credential, value|
|
62
|
-
next if value.nil? || value == true || value == false || value.is_a?(String)
|
63
|
-
raise(Twitter::Error::ConfigurationError.new("Invalid #{credential} specified: #{value.inspect} must be a String."))
|
64
|
-
end
|
49
|
+
def blank?(s)
|
50
|
+
s.respond_to?(:empty?) ? s.empty? : !s
|
65
51
|
end
|
66
52
|
end
|
67
53
|
end
|
@@ -8,8 +8,9 @@ module Twitter
|
|
8
8
|
# @return [Array<String>]
|
9
9
|
attr_reader :non_username_paths
|
10
10
|
# @return [Integer]
|
11
|
-
attr_reader :characters_reserved_per_media, :
|
12
|
-
:photo_size_limit, :short_url_length,
|
11
|
+
attr_reader :characters_reserved_per_media, :dm_text_character_limit,
|
12
|
+
:max_media_per_upload, :photo_size_limit, :short_url_length,
|
13
|
+
:short_url_length_https
|
13
14
|
alias short_uri_length short_url_length
|
14
15
|
alias short_uri_length_https short_url_length_https
|
15
16
|
|
data/lib/twitter/creatable.rb
CHANGED
data/lib/twitter/cursor.rb
CHANGED
data/lib/twitter/entity/uri.rb
CHANGED
data/lib/twitter/error.rb
CHANGED
@@ -8,6 +8,79 @@ module Twitter
|
|
8
8
|
# @return [Twitter::RateLimit]
|
9
9
|
attr_reader :rate_limit
|
10
10
|
|
11
|
+
# Raised when Twitter returns a 4xx HTTP status code
|
12
|
+
ClientError = Class.new(self)
|
13
|
+
|
14
|
+
# Raised when Twitter returns the HTTP status code 400
|
15
|
+
BadRequest = Class.new(ClientError)
|
16
|
+
|
17
|
+
# Raised when Twitter returns the HTTP status code 401
|
18
|
+
Unauthorized = Class.new(ClientError)
|
19
|
+
|
20
|
+
# Raised when Twitter returns the HTTP status code 403
|
21
|
+
Forbidden = Class.new(ClientError)
|
22
|
+
|
23
|
+
# Raised when Twitter returns the HTTP status code 413
|
24
|
+
RequestEntityTooLarge = Class.new(ClientError)
|
25
|
+
|
26
|
+
# Raised when a Tweet has already been favorited
|
27
|
+
AlreadyFavorited = Class.new(Forbidden)
|
28
|
+
|
29
|
+
# Raised when a Tweet has already been retweeted
|
30
|
+
AlreadyRetweeted = Class.new(Forbidden)
|
31
|
+
|
32
|
+
# Raised when a Tweet has already been posted
|
33
|
+
DuplicateStatus = Class.new(Forbidden)
|
34
|
+
|
35
|
+
# Raised when Twitter returns the HTTP status code 404
|
36
|
+
NotFound = Class.new(ClientError)
|
37
|
+
|
38
|
+
# Raised when Twitter returns the HTTP status code 406
|
39
|
+
NotAcceptable = Class.new(ClientError)
|
40
|
+
|
41
|
+
# Raised when Twitter returns the HTTP status code 422
|
42
|
+
UnprocessableEntity = Class.new(ClientError)
|
43
|
+
|
44
|
+
# Raised when Twitter returns the HTTP status code 429
|
45
|
+
TooManyRequests = Class.new(ClientError)
|
46
|
+
|
47
|
+
# Raised when Twitter returns a 5xx HTTP status code
|
48
|
+
ServerError = Class.new(self)
|
49
|
+
|
50
|
+
# Raised when Twitter returns the HTTP status code 500
|
51
|
+
InternalServerError = Class.new(ServerError)
|
52
|
+
|
53
|
+
# Raised when Twitter returns the HTTP status code 502
|
54
|
+
BadGateway = Class.new(ServerError)
|
55
|
+
|
56
|
+
# Raised when Twitter returns the HTTP status code 503
|
57
|
+
ServiceUnavailable = Class.new(ServerError)
|
58
|
+
|
59
|
+
# Raised when Twitter returns the HTTP status code 504
|
60
|
+
GatewayTimeout = Class.new(ServerError)
|
61
|
+
|
62
|
+
ERRORS = {
|
63
|
+
400 => Twitter::Error::BadRequest,
|
64
|
+
401 => Twitter::Error::Unauthorized,
|
65
|
+
403 => Twitter::Error::Forbidden,
|
66
|
+
404 => Twitter::Error::NotFound,
|
67
|
+
406 => Twitter::Error::NotAcceptable,
|
68
|
+
413 => Twitter::Error::RequestEntityTooLarge,
|
69
|
+
422 => Twitter::Error::UnprocessableEntity,
|
70
|
+
429 => Twitter::Error::TooManyRequests,
|
71
|
+
500 => Twitter::Error::InternalServerError,
|
72
|
+
502 => Twitter::Error::BadGateway,
|
73
|
+
503 => Twitter::Error::ServiceUnavailable,
|
74
|
+
504 => Twitter::Error::GatewayTimeout,
|
75
|
+
}.freeze
|
76
|
+
|
77
|
+
FORBIDDEN_MESSAGES = {
|
78
|
+
'Status is a duplicate.' => Twitter::Error::DuplicateStatus,
|
79
|
+
'You have already favorited this status.' => Twitter::Error::AlreadyFavorited,
|
80
|
+
'You have already retweeted this tweet.' => Twitter::Error::AlreadyRetweeted,
|
81
|
+
'sharing is not permissible for this status (Share validations failed)' => Twitter::Error::AlreadyRetweeted,
|
82
|
+
}.freeze
|
83
|
+
|
11
84
|
# If error code is missing see https://dev.twitter.com/overview/api/response-codes
|
12
85
|
module Code
|
13
86
|
AUTHENTICATION_PROBLEM = 32
|
@@ -35,49 +108,24 @@ module Twitter
|
|
35
108
|
CANNOT_MUTE = 271
|
36
109
|
CANNOT_UNMUTE = 272
|
37
110
|
end
|
38
|
-
Codes = Code
|
39
111
|
|
40
112
|
class << self
|
113
|
+
include Twitter::Utils
|
114
|
+
|
41
115
|
# Create a new error from an HTTP response
|
42
116
|
#
|
43
|
-
# @param
|
117
|
+
# @param body [String]
|
118
|
+
# @param headers [Hash]
|
44
119
|
# @return [Twitter::Error]
|
45
|
-
def from_response(
|
46
|
-
message, code = parse_error(
|
47
|
-
new(message,
|
48
|
-
end
|
49
|
-
|
50
|
-
# @return [Hash]
|
51
|
-
def errors
|
52
|
-
@errors ||= {
|
53
|
-
400 => Twitter::Error::BadRequest,
|
54
|
-
401 => Twitter::Error::Unauthorized,
|
55
|
-
403 => Twitter::Error::Forbidden,
|
56
|
-
404 => Twitter::Error::NotFound,
|
57
|
-
406 => Twitter::Error::NotAcceptable,
|
58
|
-
408 => Twitter::Error::RequestTimeout,
|
59
|
-
420 => Twitter::Error::EnhanceYourCalm,
|
60
|
-
422 => Twitter::Error::UnprocessableEntity,
|
61
|
-
429 => Twitter::Error::TooManyRequests,
|
62
|
-
500 => Twitter::Error::InternalServerError,
|
63
|
-
502 => Twitter::Error::BadGateway,
|
64
|
-
503 => Twitter::Error::ServiceUnavailable,
|
65
|
-
504 => Twitter::Error::GatewayTimeout,
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
def forbidden_messages
|
70
|
-
@forbidden_messages ||= {
|
71
|
-
'Status is a duplicate.' => Twitter::Error::DuplicateStatus,
|
72
|
-
'You have already favorited this status.' => Twitter::Error::AlreadyFavorited,
|
73
|
-
'sharing is not permissible for this status (Share validations failed)' => Twitter::Error::AlreadyRetweeted,
|
74
|
-
}
|
120
|
+
def from_response(body, headers)
|
121
|
+
message, code = parse_error(body)
|
122
|
+
new(message, headers, code)
|
75
123
|
end
|
76
124
|
|
77
125
|
private
|
78
126
|
|
79
127
|
def parse_error(body)
|
80
|
-
if body.nil?
|
128
|
+
if body.nil? || body.empty?
|
81
129
|
['', nil]
|
82
130
|
elsif body[:error]
|
83
131
|
[body[:error], nil]
|
@@ -107,68 +155,5 @@ module Twitter
|
|
107
155
|
@rate_limit = Twitter::RateLimit.new(rate_limit)
|
108
156
|
@code = code
|
109
157
|
end
|
110
|
-
|
111
|
-
ConfigurationError = Class.new(::ArgumentError)
|
112
|
-
|
113
|
-
# Raised when a Tweet includes media that doesn't have a to_io method
|
114
|
-
class UnacceptableIO < StandardError
|
115
|
-
def initialize
|
116
|
-
super('The IO object for media must respond to to_io')
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Raised when Twitter returns a 4xx HTTP status code
|
121
|
-
ClientError = Class.new(self)
|
122
|
-
|
123
|
-
# Raised when Twitter returns the HTTP status code 400
|
124
|
-
BadRequest = Class.new(ClientError)
|
125
|
-
|
126
|
-
# Raised when Twitter returns the HTTP status code 401
|
127
|
-
Unauthorized = Class.new(ClientError)
|
128
|
-
|
129
|
-
# Raised when Twitter returns the HTTP status code 403
|
130
|
-
Forbidden = Class.new(ClientError)
|
131
|
-
|
132
|
-
# Raised when a Tweet has already been favorited
|
133
|
-
AlreadyFavorited = Class.new(Forbidden)
|
134
|
-
|
135
|
-
# Raised when a Tweet has already been retweeted
|
136
|
-
AlreadyRetweeted = Class.new(Forbidden)
|
137
|
-
|
138
|
-
# Raised when a Tweet has already been posted
|
139
|
-
DuplicateStatus = Class.new(Forbidden)
|
140
|
-
AlreadyPosted = DuplicateStatus
|
141
|
-
|
142
|
-
# Raised when Twitter returns the HTTP status code 404
|
143
|
-
NotFound = Class.new(ClientError)
|
144
|
-
|
145
|
-
# Raised when Twitter returns the HTTP status code 406
|
146
|
-
NotAcceptable = Class.new(ClientError)
|
147
|
-
|
148
|
-
# Raised when Twitter returns the HTTP status code 408
|
149
|
-
RequestTimeout = Class.new(ClientError)
|
150
|
-
|
151
|
-
# Raised when Twitter returns the HTTP status code 422
|
152
|
-
UnprocessableEntity = Class.new(ClientError)
|
153
|
-
|
154
|
-
# Raised when Twitter returns the HTTP status code 429
|
155
|
-
TooManyRequests = Class.new(ClientError)
|
156
|
-
EnhanceYourCalm = TooManyRequests
|
157
|
-
RateLimited = TooManyRequests
|
158
|
-
|
159
|
-
# Raised when Twitter returns a 5xx HTTP status code
|
160
|
-
ServerError = Class.new(self)
|
161
|
-
|
162
|
-
# Raised when Twitter returns the HTTP status code 500
|
163
|
-
InternalServerError = Class.new(ServerError)
|
164
|
-
|
165
|
-
# Raised when Twitter returns the HTTP status code 502
|
166
|
-
BadGateway = Class.new(ServerError)
|
167
|
-
|
168
|
-
# Raised when Twitter returns the HTTP status code 503
|
169
|
-
ServiceUnavailable = Class.new(ServerError)
|
170
|
-
|
171
|
-
# Raised when Twitter returns the HTTP status code 504
|
172
|
-
GatewayTimeout = Class.new(ServerError)
|
173
158
|
end
|
174
159
|
end
|
data/lib/twitter/geo_results.rb
CHANGED
data/lib/twitter/headers.rb
CHANGED
@@ -8,21 +8,24 @@ module Twitter
|
|
8
8
|
@client = client
|
9
9
|
@request_method = request_method.to_sym
|
10
10
|
@uri = Addressable::URI.parse(url)
|
11
|
+
@bearer_token_request = options.delete(:bearer_token_request)
|
11
12
|
@options = options
|
12
|
-
|
13
|
+
end
|
14
|
+
|
15
|
+
def bearer_token_request?
|
16
|
+
!!@bearer_token_request
|
13
17
|
end
|
14
18
|
|
15
19
|
def oauth_auth_header
|
16
|
-
SimpleOAuth::Header.new(@request_method, @uri, @
|
20
|
+
SimpleOAuth::Header.new(@request_method, @uri, @options, @client.credentials.merge(ignore_extra_keys: true))
|
17
21
|
end
|
18
22
|
|
19
23
|
def request_headers
|
20
|
-
bearer_token_request = @options.delete(:bearer_token_request)
|
21
24
|
headers = {}
|
22
|
-
|
25
|
+
headers[:user_agent] = @client.user_agent
|
26
|
+
if bearer_token_request?
|
23
27
|
headers[:accept] = '*/*'
|
24
28
|
headers[:authorization] = bearer_token_credentials_auth_header
|
25
|
-
headers[:content_type] = 'application/x-www-form-urlencoded; charset=UTF-8'
|
26
29
|
else
|
27
30
|
headers[:authorization] = auth_header
|
28
31
|
end
|
@@ -40,18 +43,16 @@ module Twitter
|
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
46
|
+
# @return [String]
|
43
47
|
def bearer_auth_header
|
44
|
-
|
45
|
-
token = bearer_token.is_a?(Twitter::Token) && bearer_token.bearer? ? bearer_token.access_token : bearer_token
|
46
|
-
"Bearer #{token}"
|
48
|
+
"Bearer #{@client.bearer_token}"
|
47
49
|
end
|
48
50
|
|
49
51
|
# Generates authentication header for a bearer token request
|
50
52
|
#
|
51
53
|
# @return [String]
|
52
54
|
def bearer_token_credentials_auth_header
|
53
|
-
|
54
|
-
"Basic #{basic_auth_token}"
|
55
|
+
"Basic #{Base64.strict_encode64("#{@client.consumer_key}:#{@client.consumer_secret}")}"
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
data/lib/twitter/media/photo.rb
CHANGED
data/lib/twitter/media/video.rb
CHANGED
@@ -9,6 +9,10 @@ module Twitter
|
|
9
9
|
|
10
10
|
# @return [Array<Integer>]
|
11
11
|
attr_reader :indices
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :type
|
15
|
+
|
12
16
|
display_uri_attr_reader
|
13
17
|
uri_attr_reader :expanded_uri, :media_uri, :media_uri_https, :uri
|
14
18
|
|
@@ -18,7 +22,6 @@ module Twitter
|
|
18
22
|
def sizes
|
19
23
|
@attrs.fetch(:sizes, []).each_with_object({}) do |(key, value), object|
|
20
24
|
object[key] = Size.new(value)
|
21
|
-
object
|
22
25
|
end
|
23
26
|
end
|
24
27
|
memoize :sizes
|