simple_tweet 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dba99628f81892c32f4a763db2dfa62d8f0594e7596b8ba0213a1e6a26d9d217
4
- data.tar.gz: 506db3714b3c8988e136984efb9ecf6dc462915be06dd385fd9918a7c3c2d0d2
3
+ metadata.gz: 2348deca66984de880a63af33816583712ddcbb9b56237ce4c0974be3343cef4
4
+ data.tar.gz: 1681c985de747bd2192cdb78c1317148c9ac94d60fbc1e9ddd37b069f43a32cb
5
5
  SHA512:
6
- metadata.gz: 74126d63ca533d0c9d36f5bf306ab2bf2164e08465e6e652c265a44948e4a52bd758005846e3487e7216ff1e5ea13028bd370dacc546e17d54e5ef37bdd39777
7
- data.tar.gz: 8504a4a3d65f608c981710339e3158a58ab3e3f2841d03ccf32abf1744962a9c78a50d28c1fb9bf9d0e260b49f49ddae221b70c11878811205f993fc99a4778b
6
+ metadata.gz: 834a576f510f3f109a928977594cb03071bbe21d4b2c3f345b55ffc9681db1101025540642be6188a36f57b65763d284f33dbdc6392632fd53d696b5d368b34c
7
+ data.tar.gz: '09a28d5cdb58f8c71d50371a35129e1f126c9b6ca7b864af916f0fe0a1736ac0266faf26594cab069c7da6af1365e1bd5a561c14a52e2e4c6a692a45f0ec1d54'
data/README.md CHANGED
@@ -26,7 +26,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
26
26
 
27
27
  ## Contributing
28
28
 
29
- Bug reports and pull requests are welcome on GitHub at https://github.com/nna774/simple_tweet. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/nna774/simple_tweet/blob/main/CODE_OF_CONDUCT.md).
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nota/simple_tweet. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/nota/simple_tweet/blob/main/CODE_OF_CONDUCT.md).
30
30
 
31
31
  ## License
32
32
 
@@ -34,4 +34,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
34
34
 
35
35
  ## Code of Conduct
36
36
 
37
- Everyone interacting in the SimpleTweet project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/nna774/simple_tweet/blob/main/CODE_OF_CONDUCT.md).
37
+ Everyone interacting in the SimpleTweet project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/nota/simple_tweet/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,156 @@
1
+ require "cgi"
2
+ require "oauth"
3
+ require "json"
4
+ require "net/http/post/multipart"
5
+
6
+ module SimpleTweet
7
+ module V1
8
+ # Client provides only tweet
9
+ class Client
10
+ TW_API_ORIGIN = "https://api.twitter.com"
11
+ TW_UPLOAD_ORIGIN = "https://upload.twitter.com"
12
+ TW_MEDIA_UPLOAD_PATH = "/1.1/media/upload.json"
13
+ APPEND_PER = 5 * (1 << 20)
14
+
15
+ def initialize(consumer_key:, consumer_secret:, access_token:, access_token_secret:, max_append_retry: 3)
16
+ @consumer_key_ = consumer_key
17
+ @consumer_secret_ = consumer_secret
18
+ @access_token_ = access_token
19
+ @access_token_secret_ = access_token_secret
20
+ @max_append_retry_ = max_append_retry
21
+ end
22
+
23
+ # https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-update
24
+ def tweet(message:, media_ids: [])
25
+ path = "/1.1/statuses/update.json?status=#{::CGI.escape(message)}"
26
+ path += "&media_ids=#{media_ids.join(",")}" unless media_ids.empty?
27
+ Tweet.from_response(access_token.post(path))
28
+ end
29
+
30
+ # media_type is mime_type
31
+ def tweet_with_media(message:, media_type:, media:)
32
+ media_ids = upload_media(media_type: media_type, media: media)
33
+ tweet(message: message, media_ids: media_ids)
34
+ end
35
+
36
+ private
37
+
38
+ def access_token(site: TW_API_ORIGIN)
39
+ consumer = ::OAuth::Consumer.new(@consumer_key_, @consumer_secret_, site: site)
40
+ ::OAuth::AccessToken.new(consumer, @access_token_, @access_token_secret_)
41
+ end
42
+
43
+ def request(req)
44
+ @client ||= access_token(site: TW_UPLOAD_ORIGIN)
45
+ @client.sign! req
46
+
47
+ url = ::URI.parse(TW_UPLOAD_ORIGIN + TW_MEDIA_UPLOAD_PATH)
48
+ https = ::Net::HTTP.new(url.host, url.port)
49
+ https.use_ssl = true
50
+
51
+ https.start do |http|
52
+ http.request req
53
+ end
54
+ end
55
+
56
+ # https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/api-reference/post-media-upload
57
+ ## maybe todo: multiple image
58
+ def upload_media(media_type:, media:)
59
+ return upload_video(video: media) if media_type == "video/mp4"
60
+
61
+ req = ::Net::HTTP::Post::Multipart.new(
62
+ TW_MEDIA_UPLOAD_PATH,
63
+ media: ::UploadIO.new(media, media_type),
64
+ media_category: "tweet_image"
65
+ )
66
+ res = ::JSON.parse(request(req).body)
67
+ [res["media_id_string"]]
68
+ end
69
+
70
+ def init(video:)
71
+ init_req = ::Net::HTTP::Post::Multipart.new(
72
+ TW_MEDIA_UPLOAD_PATH,
73
+ command: "INIT",
74
+ total_bytes: video.size,
75
+ media_type: "video/mp4"
76
+ )
77
+ init_res = request(init_req)
78
+ raise UploadMediaError unless init_res.code == "202"
79
+
80
+ ::JSON.parse(init_res.body)
81
+ end
82
+
83
+ def append(video:, media_id:, index:, retry_count: 0)
84
+ append_req = ::Net::HTTP::Post::Multipart.new(
85
+ TW_MEDIA_UPLOAD_PATH,
86
+ command: "APPEND",
87
+ media_id: media_id,
88
+ media: video.read(APPEND_PER),
89
+ segment_index: index
90
+ )
91
+ return if request(append_req).code == "204"
92
+ raise UploadMediaError unless retry_count <= @max_append_retry_
93
+
94
+ append(video: video, media_id: media_id, index: index, retry_count: retry_count + 1)
95
+ end
96
+
97
+ def finalize(media_id:)
98
+ finalize_req = ::Net::HTTP::Post::Multipart.new(
99
+ TW_MEDIA_UPLOAD_PATH,
100
+ command: "FINALIZE",
101
+ media_id: media_id
102
+ )
103
+ finalize_res = request(finalize_req)
104
+ raise UploadMediaError unless finalize_res.code == "201"
105
+
106
+ ::JSON.parse(finalize_res.body)
107
+ end
108
+
109
+ def status(media_id:)
110
+ status_req = ::Net::HTTP::Post::Multipart.new(
111
+ TW_MEDIA_UPLOAD_PATH,
112
+ command: "STATUS",
113
+ media_id: media_id
114
+ )
115
+ status_res = request(status_req)
116
+ raise UploadMediaError unless status_res.code == "200"
117
+
118
+ ::JSON.parse(status_res.body)
119
+ end
120
+
121
+ # https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/api-reference/post-media-upload-init
122
+ def upload_video(video:)
123
+ init_res = init(video: video)
124
+ media_id = init_res["media_id_string"]
125
+
126
+ chunks_needed = (video.size - 1) / APPEND_PER + 1
127
+ chunks_needed.times do |i|
128
+ append(video: video, media_id: media_id, index: i)
129
+ end
130
+
131
+ finalize_res = finalize(media_id: media_id)
132
+
133
+ if finalize_res["processing_info"]
134
+ retry_after = finalize_res["processing_info"]["check_after_secs"] || 5
135
+ loop do
136
+ sleep retry_after
137
+
138
+ status_res = status(media_id: media_id)
139
+ raise UploadMediaError if status_res["processing_info"].nil?
140
+ break if status_res["processing_info"]["state"] == "succeeded"
141
+
142
+ if status_res["processing_info"]["state"] == "in_progress"
143
+ retry_after = status_res["processing_info"]["check_after_secs"] || 5
144
+ next
145
+ end
146
+
147
+ # status_res_json["processing_info"]["state"] == "failed"
148
+ raise UploadMediaError
149
+ end
150
+ end
151
+
152
+ [media_id]
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,21 @@
1
+ require "json"
2
+
3
+ module SimpleTweet
4
+ module V2
5
+ # mediaのuploadはapi 1.1しか用意されていないため、それを使う。
6
+ class Client < V1::Client
7
+ TW_TWEET_PATH = "/2/tweets"
8
+ UA = "SimpleTweet/#{SimpleTweet::VERSION}"
9
+
10
+ def tweet(message:, media_ids: [])
11
+ json = {
12
+ text: message,
13
+ }
14
+ json[:media] = { media_ids: media_ids } unless media_ids.empty?
15
+ header = { "User-Agent": UA, "content-type": "application/json" }
16
+ res = access_token.post(TW_TWEET_PATH, json.to_json, header)
17
+ ::JSON.parse(res.body)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleTweet
4
- VERSION = "1.0.0"
4
+ VERSION = "2.0.0"
5
5
  end
data/lib/simple_tweet.rb CHANGED
@@ -1,163 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "simple_tweet/version"
4
- require "cgi"
5
- require "oauth"
4
+ require_relative "simple_tweet/v1_client"
5
+ require_relative "simple_tweet/v2_client"
6
6
  require "json"
7
- require "net/http/post/multipart"
8
7
 
9
8
  module SimpleTweet
10
9
  class Error < ::StandardError; end
11
10
  class UploadMediaError < Error; end
12
11
 
13
- # Client provides only tweet
14
- class Client
15
- TW_API_ORIGIN = "https://api.twitter.com"
16
- TW_UPLOAD_ORIGIN = "https://upload.twitter.com"
17
- TW_MEDIA_UPLOAD_PATH = "/1.1/media/upload.json"
18
- APPEND_PER = 5 * (1 << 20)
19
-
20
- def initialize(consumer_key:, consumer_secret:, access_token:, access_token_secret:, max_append_retry: 3)
21
- @consumer_key_ = consumer_key
22
- @consumer_secret_ = consumer_secret
23
- @access_token_ = access_token
24
- @access_token_secret_ = access_token_secret
25
- @max_append_retry_ = max_append_retry
26
- end
27
-
28
- # https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-update
29
- def tweet(message:, media_ids: [])
30
- path = "/1.1/statuses/update.json?status=#{::CGI.escape(message)}"
31
- path += "&media_ids=#{media_ids.join(",")}" unless media_ids.empty?
32
- Tweet.from_response(access_token.post(path))
33
- end
34
-
35
- # media_type is mime_type
36
- def tweet_with_media(message:, media_type:, media:)
37
- media_ids = upload_media(media_type: media_type, media: media)
38
- tweet(message: message, media_ids: media_ids)
39
- end
40
-
41
- private
42
-
43
- def access_token(site: TW_API_ORIGIN)
44
- consumer = ::OAuth::Consumer.new(@consumer_key_, @consumer_secret_, site: site)
45
- ::OAuth::AccessToken.new(consumer, @access_token_, @access_token_secret_)
46
- end
47
-
48
- def request(req)
49
- @client ||= access_token(site: TW_UPLOAD_ORIGIN)
50
- @client.sign! req
51
-
52
- url = ::URI.parse(TW_UPLOAD_ORIGIN + TW_MEDIA_UPLOAD_PATH)
53
- https = ::Net::HTTP.new(url.host, url.port)
54
- https.use_ssl = true
55
-
56
- https.start do |http|
57
- http.request req
58
- end
59
- end
60
-
61
- # https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/api-reference/post-media-upload
62
- ## maybe todo: multiple image
63
- def upload_media(media_type:, media:)
64
- return upload_video(video: media) if media_type == "video/mp4"
65
-
66
- req = ::Net::HTTP::Post::Multipart.new(
67
- TW_MEDIA_UPLOAD_PATH,
68
- media: ::UploadIO.new(media, media_type),
69
- media_category: "tweet_image"
70
- )
71
- res = ::JSON.parse(request(req).body)
72
- [res["media_id_string"]]
73
- end
74
-
75
- def init(video:)
76
- init_req = ::Net::HTTP::Post::Multipart.new(
77
- TW_MEDIA_UPLOAD_PATH,
78
- command: "INIT",
79
- total_bytes: video.size,
80
- media_type: "video/mp4"
81
- )
82
- init_res = request(init_req)
83
- raise UploadMediaError unless init_res.code == "202"
84
-
85
- ::JSON.parse(init_res.body)
86
- end
87
-
88
- def append(video:, media_id:, index:, retry_count: 0)
89
- append_req = ::Net::HTTP::Post::Multipart.new(
90
- TW_MEDIA_UPLOAD_PATH,
91
- command: "APPEND",
92
- media_id: media_id,
93
- media: video.read(APPEND_PER),
94
- segment_index: index
95
- )
96
- return if request(append_req).code == "204"
97
- raise UploadMediaError unless retry_count <= @max_append_retry_
98
-
99
- append(video: video, media_id: media_id, index: index, retry_count: retry_count + 1)
100
- end
101
-
102
- def finalize(media_id:)
103
- finalize_req = ::Net::HTTP::Post::Multipart.new(
104
- TW_MEDIA_UPLOAD_PATH,
105
- command: "FINALIZE",
106
- media_id: media_id
107
- )
108
- finalize_res = request(finalize_req)
109
- raise UploadMediaError unless finalize_res.code == "201"
110
-
111
- ::JSON.parse(finalize_res.body)
112
- end
113
-
114
- def status(media_id:)
115
- status_req = ::Net::HTTP::Post::Multipart.new(
116
- TW_MEDIA_UPLOAD_PATH,
117
- command: "STATUS",
118
- media_id: media_id
119
- )
120
- status_res = request(status_req)
121
- raise UploadMediaError unless status_res.code == "200"
122
-
123
- ::JSON.parse(status_res.body)
124
- end
125
-
126
- # https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/api-reference/post-media-upload-init
127
- def upload_video(video:)
128
- init_res = init(video: video)
129
- media_id = init_res["media_id_string"]
130
-
131
- chunks_needed = (video.size - 1) / APPEND_PER + 1
132
- chunks_needed.times do |i|
133
- append(video: video, media_id: media_id, index: i)
134
- end
135
-
136
- finalize_res = finalize(media_id: media_id)
137
-
138
- if finalize_res["processing_info"]
139
- retry_after = finalize_res["processing_info"]["check_after_secs"] || 5
140
- loop do
141
- sleep retry_after
142
-
143
- status_res = status(media_id: media_id)
144
- raise UploadMediaError if status_res["processing_info"].nil?
145
- break if status_res["processing_info"]["state"] == "succeeded"
146
-
147
- if status_res["processing_info"]["state"] == "in_progress"
148
- retry_after = status_res["processing_info"]["check_after_secs"] || 5
149
- next
150
- end
151
-
152
- # status_res_json["processing_info"]["state"] == "failed"
153
- raise UploadMediaError
154
- end
155
- end
156
-
157
- [media_id]
158
- end
159
- end
160
-
161
12
  # Twitter::Tweetに近いinterfaceを提供する。
162
13
  ## 使ってない部分のhashの中身のhashとかは正規化されてないので、必要になったら足す必要がある。
163
14
  ### todo: entities, media, ...
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_tweet
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kugayama Nana
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-02-15 00:00:00.000000000 Z
11
+ date: 2023-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multipart-post
@@ -53,6 +53,8 @@ files:
53
53
  - README.md
54
54
  - Rakefile
55
55
  - lib/simple_tweet.rb
56
+ - lib/simple_tweet/v1_client.rb
57
+ - lib/simple_tweet/v2_client.rb
56
58
  - lib/simple_tweet/version.rb
57
59
  - sig/simple_tweet.rbs
58
60
  - simple_tweet.gemspec