twitter 6.2.0 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -7
- data/LICENSE.md +1 -1
- data/README.md +9 -12
- data/lib/twitter.rb +5 -0
- data/lib/twitter/client.rb +5 -5
- data/lib/twitter/creatable.rb +5 -1
- data/lib/twitter/cursor.rb +12 -2
- data/lib/twitter/direct_message.rb +2 -0
- data/lib/twitter/direct_message_event.rb +44 -0
- data/lib/twitter/direct_messages/welcome_message.rb +17 -0
- data/lib/twitter/direct_messages/welcome_message_rule.rb +12 -0
- data/lib/twitter/direct_messages/welcome_message_rule_wrapper.rb +36 -0
- data/lib/twitter/direct_messages/welcome_message_wrapper.rb +42 -0
- data/lib/twitter/enumerable.rb +14 -3
- data/lib/twitter/error.rb +48 -6
- data/lib/twitter/null_object.rb +15 -1
- data/lib/twitter/premium_search_results.rb +67 -0
- data/lib/twitter/profile.rb +2 -2
- data/lib/twitter/rate_limit.rb +2 -2
- data/lib/twitter/rest/account_activity.rb +99 -0
- data/lib/twitter/rest/api.rb +6 -0
- data/lib/twitter/rest/direct_messages.rb +131 -42
- data/lib/twitter/rest/direct_messages/welcome_messages.rb +90 -0
- data/lib/twitter/rest/favorites.rb +2 -2
- data/lib/twitter/rest/lists.rb +1 -0
- data/lib/twitter/rest/premium_search.rb +34 -0
- data/lib/twitter/rest/request.rb +39 -13
- data/lib/twitter/rest/search.rb +4 -3
- data/lib/twitter/rest/timelines.rb +1 -0
- data/lib/twitter/rest/tweets.rb +2 -31
- data/lib/twitter/rest/upload_utils.rb +68 -0
- data/lib/twitter/rest/utils.rb +24 -7
- data/lib/twitter/search_results.rb +4 -3
- data/lib/twitter/streaming/client.rb +5 -1
- data/lib/twitter/streaming/connection.rb +16 -3
- data/lib/twitter/streaming/message_parser.rb +1 -1
- data/lib/twitter/streaming/response.rb +1 -0
- data/lib/twitter/tweet.rb +10 -1
- data/lib/twitter/utils.rb +4 -2
- data/lib/twitter/version.rb +2 -2
- data/twitter.gemspec +3 -4
- metadata +16 -21
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'twitter/arguments'
|
2
|
+
require 'twitter/rest/upload_utils'
|
3
|
+
require 'twitter/rest/utils'
|
4
|
+
require 'twitter/utils'
|
5
|
+
|
6
|
+
module Twitter
|
7
|
+
module REST
|
8
|
+
module DirectMessages
|
9
|
+
module WelcomeMessages
|
10
|
+
include Twitter::REST::UploadUtils
|
11
|
+
include Twitter::REST::Utils
|
12
|
+
include Twitter::Utils
|
13
|
+
|
14
|
+
# Welcome Message
|
15
|
+
|
16
|
+
def create_welcome_message(text, name = nil, options = {})
|
17
|
+
json_options = {
|
18
|
+
welcome_message: {
|
19
|
+
message_data: {
|
20
|
+
text: text,
|
21
|
+
},
|
22
|
+
},
|
23
|
+
}
|
24
|
+
json_options[:welcome_message][:name] = name if name
|
25
|
+
welcome_message_wrapper = perform_request_with_object(:json_post, '/1.1/direct_messages/welcome_messages/new.json', json_options.merge!(options), Twitter::DirectMessages::WelcomeMessageWrapper)
|
26
|
+
welcome_message_wrapper.welcome_message
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy_welcome_message(*ids)
|
30
|
+
perform_requests(:delete, '/1.1/direct_messages/welcome_messages/destroy.json', ids)
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_welcome_message(welcome_message_id, text, options = {})
|
34
|
+
params = {
|
35
|
+
id: welcome_message_id,
|
36
|
+
}
|
37
|
+
json_options = {
|
38
|
+
message_data: {
|
39
|
+
text: text,
|
40
|
+
},
|
41
|
+
}
|
42
|
+
welcome_message_wrapper = perform_request_with_object(:json_put, '/1.1/direct_messages/welcome_messages/update.json', json_options.merge!(options), Twitter::DirectMessages::WelcomeMessageWrapper, params)
|
43
|
+
welcome_message_wrapper.welcome_message
|
44
|
+
end
|
45
|
+
|
46
|
+
def welcome_message(id, options = {})
|
47
|
+
options = options.dup
|
48
|
+
options[:id] = id
|
49
|
+
welcome_message_wrapper = perform_get_with_object('/1.1/direct_messages/welcome_messages/show.json', options, Twitter::DirectMessages::WelcomeMessageWrapper)
|
50
|
+
welcome_message_wrapper.welcome_message
|
51
|
+
end
|
52
|
+
|
53
|
+
def welcome_message_list(options = {})
|
54
|
+
limit = options.fetch(:count, 20)
|
55
|
+
welcome_message_wrappers = perform_get_with_cursor('/1.1/direct_messages/welcome_messages/list.json', options.merge!(no_default_cursor: true, count: 50, limit: limit), :welcome_messages, Twitter::DirectMessages::WelcomeMessageWrapper)
|
56
|
+
welcome_message_wrappers.collect(&:welcome_message)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Welcome Message Rule
|
60
|
+
|
61
|
+
def create_welcome_message_rule(welcome_message_id, options = {})
|
62
|
+
json_options = {
|
63
|
+
welcome_message_rule: {
|
64
|
+
welcome_message_id: welcome_message_id,
|
65
|
+
},
|
66
|
+
}
|
67
|
+
rule_wrapper = perform_request_with_object(:json_post, '/1.1/direct_messages/welcome_messages/rules/new.json', json_options.merge!(options), Twitter::DirectMessages::WelcomeMessageRuleWrapper)
|
68
|
+
rule_wrapper.welcome_message_rule
|
69
|
+
end
|
70
|
+
|
71
|
+
def destroy_welcome_message_rule(*ids)
|
72
|
+
perform_requests(:delete, '/1.1/direct_messages/welcome_messages/rules/destroy.json', ids)
|
73
|
+
end
|
74
|
+
|
75
|
+
def welcome_message_rule(id, options = {})
|
76
|
+
options = options.dup
|
77
|
+
options[:id] = id
|
78
|
+
rule_wrapper = perform_get_with_object('/1.1/direct_messages/welcome_messages/rules/show.json', options, Twitter::DirectMessages::WelcomeMessageRuleWrapper)
|
79
|
+
rule_wrapper.welcome_message_rule
|
80
|
+
end
|
81
|
+
|
82
|
+
def welcome_message_rule_list(options = {})
|
83
|
+
limit = options.fetch(:count, 20)
|
84
|
+
rule_wrappers = perform_get_with_cursor('/1.1/direct_messages/welcome_messages/rules/list.json', options.merge!(no_default_cursor: true, count: 50, limit: limit), :welcome_message_rules, Twitter::DirectMessages::WelcomeMessageRuleWrapper)
|
85
|
+
rule_wrappers.collect(&:welcome_message_rule)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -20,14 +20,14 @@ module Twitter
|
|
20
20
|
# Returns the 20 most recent favorite Tweets for the authenticating user
|
21
21
|
#
|
22
22
|
# @param options [Hash] A customizable set of options.
|
23
|
-
# @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to
|
23
|
+
# @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 200.
|
24
24
|
# @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID.
|
25
25
|
# @overload favorites(user, options = {})
|
26
26
|
# Returns the 20 most recent favorite Tweets for the specified user
|
27
27
|
#
|
28
28
|
# @param user [Integer, String, Twitter::User] A Twitter user ID, screen name, URI, or object.
|
29
29
|
# @param options [Hash] A customizable set of options.
|
30
|
-
# @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to
|
30
|
+
# @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 200.
|
31
31
|
# @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID.
|
32
32
|
def favorites(*args)
|
33
33
|
arguments = Twitter::Arguments.new(args)
|
data/lib/twitter/rest/lists.rb
CHANGED
@@ -484,6 +484,7 @@ module Twitter
|
|
484
484
|
# @return [Hash]
|
485
485
|
def merge_owner!(hash, user)
|
486
486
|
return hash if hash[:owner_id] || hash[:owner_screen_name]
|
487
|
+
|
487
488
|
if user
|
488
489
|
merge_user!(hash, user, 'owner')
|
489
490
|
hash[:owner_id] = hash.delete(:owner_user_id) unless hash[:owner_user_id].nil?
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'twitter/rest/request'
|
2
|
+
require 'twitter/premium_search_results'
|
3
|
+
|
4
|
+
module Twitter
|
5
|
+
module REST
|
6
|
+
module PremiumSearch
|
7
|
+
MAX_TWEETS_PER_REQUEST = 100
|
8
|
+
|
9
|
+
# Returns tweets from the 30-Day API that match a specified query.
|
10
|
+
#
|
11
|
+
# @see https://developer.twitter.com/en/docs/tweets/search/overview/premium
|
12
|
+
# @see https://developer.twitter.com/en/docs/tweets/search/api-reference/premium-search.html#DataEndpoint
|
13
|
+
# @rate_limited Yes
|
14
|
+
# @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
|
15
|
+
# @param query [String] A search term.
|
16
|
+
# @param options [Hash] A customizable set of options.
|
17
|
+
# @option options [String] :tag Tags can be used to segregate rules and their matching data into different logical groups.
|
18
|
+
# @option options [Integer] :maxResults The maximum number of search results to be returned by a request. A number between 10 and the system limit (currently 500, 100 for Sandbox environments). By default, a request response will return 100 results
|
19
|
+
# @option options [String] :fromDate The oldest UTC timestamp (from most recent 30 days) from which the Tweets will be provided. Date should be formatted as yyyymmddhhmm.
|
20
|
+
# @option options [String] :toDate The latest, most recent UTC timestamp to which the activities will be provided. Date should be formatted as yyyymmddhhmm.
|
21
|
+
# @option request_config [String] :product Indicates the search endpoint you are making requests to, either 30day or fullarchive. Default 30day
|
22
|
+
# @return [Twitter::PremiumSearchResults] Return tweets that match a specified query with search metadata
|
23
|
+
def premium_search(query, options = {}, request_config = {})
|
24
|
+
options = options.clone
|
25
|
+
options[:maxResults] ||= MAX_TWEETS_PER_REQUEST
|
26
|
+
request_config[:request_method] = :json_post if request_config[:request_method].nil? || request_config[:request_method] == :post
|
27
|
+
request_config[:product] ||= '30day'
|
28
|
+
path = "/1.1/tweets/search/#{request_config[:product]}/#{dev_environment}.json"
|
29
|
+
request = Twitter::REST::Request.new(self, request_config[:request_method], path, options.merge(query: query))
|
30
|
+
Twitter::PremiumSearchResults.new(request, request_config)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/twitter/rest/request.rb
CHANGED
@@ -10,7 +10,7 @@ require 'twitter/utils'
|
|
10
10
|
|
11
11
|
module Twitter
|
12
12
|
module REST
|
13
|
-
class Request
|
13
|
+
class Request # rubocop:disable Metrics/ClassLength
|
14
14
|
include Twitter::Utils
|
15
15
|
BASE_URL = 'https://api.twitter.com'.freeze
|
16
16
|
attr_accessor :client, :headers, :options, :path, :rate_limit,
|
@@ -22,18 +22,20 @@ module Twitter
|
|
22
22
|
# @param path [String]
|
23
23
|
# @param options [Hash]
|
24
24
|
# @return [Twitter::REST::Request]
|
25
|
-
def initialize(client, request_method, path, options = {})
|
25
|
+
def initialize(client, request_method, path, options = {}, params = nil)
|
26
26
|
@client = client
|
27
27
|
@uri = Addressable::URI.parse(path.start_with?('http') ? path : BASE_URL + path)
|
28
|
-
|
28
|
+
multipart_options = params || options
|
29
|
+
set_multipart_options!(request_method, multipart_options)
|
29
30
|
@path = uri.path
|
30
31
|
@options = options
|
32
|
+
@options_key = {get: :params, json_post: :json, json_put: :json, delete: :params}[request_method] || :form
|
33
|
+
@params = params
|
31
34
|
end
|
32
35
|
|
33
36
|
# @return [Array, Hash]
|
34
37
|
def perform
|
35
|
-
|
36
|
-
response = http_client.headers(@headers).public_send(@request_method, @uri.to_s, options_key => @options)
|
38
|
+
response = http_client.headers(@headers).public_send(@request_method, @uri.to_s, request_options)
|
37
39
|
response_body = response.body.empty? ? '' : symbolize_keys!(response.parse)
|
38
40
|
response_headers = response.headers
|
39
41
|
fail_or_return_response_body(response.code, response_body, response_headers)
|
@@ -41,29 +43,43 @@ module Twitter
|
|
41
43
|
|
42
44
|
private
|
43
45
|
|
46
|
+
def request_options
|
47
|
+
options = {@options_key => @options}
|
48
|
+
if @params
|
49
|
+
if options[:params]
|
50
|
+
options[:params].merge(@params)
|
51
|
+
else
|
52
|
+
options[:params] = @params
|
53
|
+
end
|
54
|
+
end
|
55
|
+
options
|
56
|
+
end
|
57
|
+
|
44
58
|
def merge_multipart_file!(options)
|
45
59
|
key = options.delete(:key)
|
46
60
|
file = options.delete(:file)
|
47
61
|
|
48
62
|
options[key] = if file.is_a?(StringIO)
|
49
|
-
HTTP::FormData::File.new(file,
|
63
|
+
HTTP::FormData::File.new(file, content_type: 'video/mp4')
|
50
64
|
else
|
51
|
-
HTTP::FormData::File.new(file, filename: File.basename(file),
|
65
|
+
HTTP::FormData::File.new(file, filename: File.basename(file), content_type: content_type(File.basename(file)))
|
52
66
|
end
|
53
67
|
end
|
54
68
|
|
55
69
|
def set_multipart_options!(request_method, options)
|
56
|
-
if request_method
|
57
|
-
merge_multipart_file!(options)
|
70
|
+
if %i[multipart_post json_post].include?(request_method)
|
71
|
+
merge_multipart_file!(options) if request_method == :multipart_post
|
72
|
+
options = {}
|
58
73
|
@request_method = :post
|
59
|
-
|
74
|
+
elsif request_method == :json_put
|
75
|
+
@request_method = :put
|
60
76
|
else
|
61
77
|
@request_method = request_method
|
62
|
-
@headers = Twitter::Headers.new(@client, @request_method, @uri, options).request_headers
|
63
78
|
end
|
79
|
+
@headers = Twitter::Headers.new(@client, @request_method, @uri, options).request_headers
|
64
80
|
end
|
65
81
|
|
66
|
-
def
|
82
|
+
def content_type(basename)
|
67
83
|
case basename
|
68
84
|
when /\.gif$/i
|
69
85
|
'image/gif'
|
@@ -79,6 +95,7 @@ module Twitter
|
|
79
95
|
def fail_or_return_response_body(code, body, headers)
|
80
96
|
error = error(code, body, headers)
|
81
97
|
raise(error) if error
|
98
|
+
|
82
99
|
@rate_limit = Twitter::RateLimit.new(headers)
|
83
100
|
body
|
84
101
|
end
|
@@ -89,6 +106,8 @@ module Twitter
|
|
89
106
|
forbidden_error(body, headers)
|
90
107
|
elsif !klass.nil?
|
91
108
|
klass.from_response(body, headers)
|
109
|
+
elsif body&.is_a?(Hash) && (err = body.dig(:processing_info, :error))
|
110
|
+
Twitter::Error::MediaError.from_processing_response(err, headers)
|
92
111
|
end
|
93
112
|
end
|
94
113
|
|
@@ -115,10 +134,17 @@ module Twitter
|
|
115
134
|
object
|
116
135
|
end
|
117
136
|
|
137
|
+
# Returns boolean indicating if all the keys required by HTTP::Client are present in Twitter::Client#timeouts
|
138
|
+
#
|
139
|
+
# @return [Boolean]
|
140
|
+
def timeout_keys_defined
|
141
|
+
(%i[write connect read] - (@client.timeouts&.keys || [])).empty?
|
142
|
+
end
|
143
|
+
|
118
144
|
# @return [HTTP::Client, HTTP]
|
119
145
|
def http_client
|
120
146
|
client = @client.proxy ? HTTP.via(*proxy) : HTTP
|
121
|
-
client = client.timeout(
|
147
|
+
client = client.timeout(connect: @client.timeouts[:connect], read: @client.timeouts[:read], write: @client.timeouts[:write]) if timeout_keys_defined
|
122
148
|
client
|
123
149
|
end
|
124
150
|
|
data/lib/twitter/rest/search.rb
CHANGED
@@ -14,7 +14,7 @@ module Twitter
|
|
14
14
|
# @rate_limited Yes
|
15
15
|
# @authentication Requires user context
|
16
16
|
# @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
|
17
|
-
# @param
|
17
|
+
# @param query [String] A search term.
|
18
18
|
# @param options [Hash] A customizable set of options.
|
19
19
|
# @option options [String] :geocode Returns tweets by users located within a given radius of the given latitude/longitude. The location is preferentially taking from the Geotagging API, but will fall back to their Twitter profile. The parameter value is specified by "latitude,longitude,radius", where radius units must be specified as either "mi" (miles) or "km" (kilometers). Note that you cannot use the near operator via the API to geocode arbitrary locations; however you can use this geocode parameter to search near geocodes directly.
|
20
20
|
# @option options [String] :lang Restricts tweets to the given language, given by an ISO 639-1 code.
|
@@ -25,11 +25,12 @@ module Twitter
|
|
25
25
|
# @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID. There are limits to the number of Tweets which can be accessed through the API. If the limit of Tweets has occured since the since_id, the since_id will be forced to the oldest ID available.
|
26
26
|
# @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
|
27
27
|
# @option options [Boolean] :include_entities The entities node will be disincluded when set to false.
|
28
|
+
# @option options [String] :tweet_mode The entities node will truncate or not tweet text. Options are "compat" and "extended". The current default is "compat" (truncate).
|
28
29
|
# @return [Twitter::SearchResults] Return tweets that match a specified query with search metadata
|
29
|
-
def search(
|
30
|
+
def search(query, options = {})
|
30
31
|
options = options.dup
|
31
32
|
options[:count] ||= MAX_TWEETS_PER_REQUEST
|
32
|
-
request = Twitter::REST::Request.new(self, :get, '/1.1/search/tweets.json', options.merge(q:
|
33
|
+
request = Twitter::REST::Request.new(self, :get, '/1.1/search/tweets.json', options.merge(q: query))
|
33
34
|
Twitter::SearchResults.new(request)
|
34
35
|
end
|
35
36
|
end
|
@@ -188,6 +188,7 @@ module Twitter
|
|
188
188
|
def collect_with_max_id(collection = [], max_id = nil, &block)
|
189
189
|
tweets = yield(max_id)
|
190
190
|
return collection if tweets.nil?
|
191
|
+
|
191
192
|
collection += tweets
|
192
193
|
tweets.empty? ? collection.flatten : collect_with_max_id(collection, tweets.last.id - 1, &block)
|
193
194
|
end
|
data/lib/twitter/rest/tweets.rb
CHANGED
@@ -2,6 +2,7 @@ require 'twitter/arguments'
|
|
2
2
|
require 'twitter/error'
|
3
3
|
require 'twitter/oembed'
|
4
4
|
require 'twitter/rest/request'
|
5
|
+
require 'twitter/rest/upload_utils'
|
5
6
|
require 'twitter/rest/utils'
|
6
7
|
require 'twitter/tweet'
|
7
8
|
require 'twitter/utils'
|
@@ -9,6 +10,7 @@ require 'twitter/utils'
|
|
9
10
|
module Twitter
|
10
11
|
module REST
|
11
12
|
module Tweets
|
13
|
+
include Twitter::REST::UploadUtils
|
12
14
|
include Twitter::REST::Utils
|
13
15
|
include Twitter::Utils
|
14
16
|
MAX_TWEETS_PER_REQUEST = 100
|
@@ -323,37 +325,6 @@ module Twitter
|
|
323
325
|
|
324
326
|
private
|
325
327
|
|
326
|
-
# Uploads images and videos. Videos require multiple requests and uploads in chunks of 5 Megabytes.
|
327
|
-
# The only supported video format is mp4.
|
328
|
-
#
|
329
|
-
# @see https://dev.twitter.com/rest/public/uploading-media
|
330
|
-
def upload(media) # rubocop:disable MethodLength, AbcSize
|
331
|
-
if File.basename(media) !~ /\.mp4$/
|
332
|
-
Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json', key: :media, file: media).perform
|
333
|
-
else
|
334
|
-
init = Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
|
335
|
-
command: 'INIT',
|
336
|
-
media_type: 'video/mp4',
|
337
|
-
total_bytes: media.size).perform
|
338
|
-
|
339
|
-
until media.eof?
|
340
|
-
chunk = media.read(5_000_000)
|
341
|
-
seg ||= -1
|
342
|
-
Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json',
|
343
|
-
command: 'APPEND',
|
344
|
-
media_id: init[:media_id],
|
345
|
-
segment_index: seg += 1,
|
346
|
-
key: :media,
|
347
|
-
file: StringIO.new(chunk)).perform
|
348
|
-
end
|
349
|
-
|
350
|
-
media.close
|
351
|
-
|
352
|
-
Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
|
353
|
-
command: 'FINALIZE', media_id: init[:media_id]).perform
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
328
|
def array_wrap(object)
|
358
329
|
if object.respond_to?(:to_ary)
|
359
330
|
object.to_ary || [object]
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'twitter/rest/request'
|
2
|
+
|
3
|
+
module Twitter
|
4
|
+
module REST
|
5
|
+
module UploadUtils
|
6
|
+
private
|
7
|
+
|
8
|
+
# Uploads images and videos. Videos require multiple requests and uploads in chunks of 5 Megabytes.
|
9
|
+
# The only supported video format is mp4.
|
10
|
+
#
|
11
|
+
# @see https://developer.twitter.com/en/docs/media/upload-media/uploading-media/media-best-practices
|
12
|
+
def upload(media, media_category_prefix: 'tweet')
|
13
|
+
return chunk_upload(media, 'video/mp4', "#{media_category_prefix}_video") if File.extname(media) == '.mp4'
|
14
|
+
return chunk_upload(media, 'image/gif', "#{media_category_prefix}_gif") if File.extname(media) == '.gif' && File.size(media) > 5_000_000
|
15
|
+
|
16
|
+
Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json', key: :media, file: media).perform
|
17
|
+
end
|
18
|
+
|
19
|
+
# @raise [Twitter::Error::TimeoutError] Error raised when the upload is longer than the value specified in Twitter::Client#timeouts[:upload].
|
20
|
+
# @raise [Twitter::Error::MediaError] Error raised when Twitter return an error about a media which is not mapped by the gem.
|
21
|
+
# @raise [Twitter::Error::MediaInternalError] Error raised when Twitter returns an InternalError error.
|
22
|
+
# @raise [Twitter::Error::InvalidMedia] Error raised when Twitter returns an InvalidMedia error.
|
23
|
+
# @raise [Twitter::Error::UnsupportedMedia] Error raised when Twitter returns an UnsupportedMedia error.
|
24
|
+
# @see https://developer.twitter.com/en/docs/media/upload-media/uploading-media/chunked-media-upload
|
25
|
+
def chunk_upload(media, media_type, media_category)
|
26
|
+
Timeout.timeout(timeouts&.fetch(:upload, nil), Twitter::Error::TimeoutError) do
|
27
|
+
init = Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
|
28
|
+
command: 'INIT',
|
29
|
+
media_type: media_type,
|
30
|
+
media_category: media_category,
|
31
|
+
total_bytes: media.size).perform
|
32
|
+
append_media(media, init[:media_id])
|
33
|
+
media.close
|
34
|
+
finalize_media(init[:media_id])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @see https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-append
|
39
|
+
def append_media(media, media_id)
|
40
|
+
until media.eof?
|
41
|
+
chunk = media.read(5_000_000)
|
42
|
+
seg ||= -1
|
43
|
+
Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json',
|
44
|
+
command: 'APPEND',
|
45
|
+
media_id: media_id,
|
46
|
+
segment_index: seg += 1,
|
47
|
+
key: :media,
|
48
|
+
file: StringIO.new(chunk)).perform
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @see https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-finalize
|
53
|
+
# @see https://developer.twitter.com/en/docs/media/upload-media/api-reference/get-media-upload-status
|
54
|
+
def finalize_media(media_id)
|
55
|
+
response = Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
|
56
|
+
command: 'FINALIZE', media_id: media_id).perform
|
57
|
+
loop do
|
58
|
+
return response if !response[:processing_info] || %w[failed succeeded].include?(response[:processing_info][:state])
|
59
|
+
|
60
|
+
sleep(response[:processing_info][:check_after_secs])
|
61
|
+
response = Twitter::REST::Request.new(self, :get, 'https://upload.twitter.com/1.1/media/upload.json',
|
62
|
+
command: 'STATUS', media_id: media_id).perform
|
63
|
+
end
|
64
|
+
response
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/twitter/rest/utils.rb
CHANGED
@@ -46,8 +46,8 @@ module Twitter
|
|
46
46
|
# @param request_method [Symbol]
|
47
47
|
# @param path [String]
|
48
48
|
# @param options [Hash]
|
49
|
-
def perform_request(request_method, path, options = {})
|
50
|
-
Twitter::REST::Request.new(self, request_method, path, options).perform
|
49
|
+
def perform_request(request_method, path, options = {}, params = nil)
|
50
|
+
Twitter::REST::Request.new(self, request_method, path, options, params).perform
|
51
51
|
end
|
52
52
|
|
53
53
|
# @param path [String]
|
@@ -68,8 +68,8 @@ module Twitter
|
|
68
68
|
# @param path [String]
|
69
69
|
# @param options [Hash]
|
70
70
|
# @param klass [Class]
|
71
|
-
def perform_request_with_object(request_method, path, options, klass)
|
72
|
-
response = perform_request(request_method, path, options)
|
71
|
+
def perform_request_with_object(request_method, path, options, klass, params = nil)
|
72
|
+
response = perform_request(request_method, path, options, params)
|
73
73
|
klass.new(response)
|
74
74
|
end
|
75
75
|
|
@@ -102,9 +102,15 @@ module Twitter
|
|
102
102
|
# @param collection_name [Symbol]
|
103
103
|
# @param klass [Class]
|
104
104
|
def perform_get_with_cursor(path, options, collection_name, klass = nil)
|
105
|
-
|
105
|
+
limit = options.delete(:limit)
|
106
|
+
if options[:no_default_cursor]
|
107
|
+
options.delete(:no_default_cursor)
|
108
|
+
else
|
109
|
+
merge_default_cursor!(options)
|
110
|
+
end
|
111
|
+
|
106
112
|
request = Twitter::REST::Request.new(self, :get, path, options)
|
107
|
-
Twitter::Cursor.new(collection_name.to_sym, klass, request)
|
113
|
+
Twitter::Cursor.new(collection_name.to_sym, klass, request, limit)
|
108
114
|
end
|
109
115
|
|
110
116
|
# @param request_method [Symbol]
|
@@ -151,6 +157,17 @@ module Twitter
|
|
151
157
|
end
|
152
158
|
end
|
153
159
|
|
160
|
+
# @param request_method [Symbol]
|
161
|
+
# @param path [String]
|
162
|
+
# @param ids [Array]
|
163
|
+
# @return nil
|
164
|
+
def perform_requests(request_method, path, ids)
|
165
|
+
ids.each do |id|
|
166
|
+
perform_request(request_method, path, id: id)
|
167
|
+
end
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
154
171
|
# @param collection_name [Symbol]
|
155
172
|
# @param klass [Class]
|
156
173
|
# @param path [String]
|
@@ -229,7 +246,7 @@ module Twitter
|
|
229
246
|
hash[:screen_name] = screen_names.join(',') unless screen_names.empty?
|
230
247
|
end
|
231
248
|
|
232
|
-
def collect_users(users) # rubocop:disable MethodLength
|
249
|
+
def collect_users(users) # rubocop:disable Metrics/MethodLength
|
233
250
|
user_ids = []
|
234
251
|
screen_names = []
|
235
252
|
users.each do |user|
|