tumblr-rb 1.3.0 → 2.0.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +7 -0
- data/Gemfile +15 -8
- data/Gemfile.lock +65 -65
- data/LICENSE +4 -2
- data/README.md +31 -84
- data/Rakefile +8 -68
- data/bin/tumblr +3 -133
- data/lib/tumblr.rb +7 -184
- data/lib/tumblr/authentication.rb +71 -0
- data/lib/tumblr/client.rb +148 -0
- data/lib/tumblr/command_line_interface.rb +222 -0
- data/lib/tumblr/credentials.rb +31 -0
- data/lib/tumblr/post.rb +253 -171
- data/lib/tumblr/post/answer.rb +17 -0
- data/lib/tumblr/post/audio.rb +22 -10
- data/lib/tumblr/post/chat.rb +25 -0
- data/lib/tumblr/post/link.rb +22 -9
- data/lib/tumblr/post/photo.rb +29 -10
- data/lib/tumblr/post/quote.rb +17 -10
- data/lib/tumblr/post/text.rb +18 -0
- data/lib/tumblr/post/video.rb +26 -11
- data/lib/tumblr/version.rb +3 -0
- data/lib/tumblr/views/error.erb +6 -0
- data/lib/tumblr/views/form.erb +11 -0
- data/lib/tumblr/views/layout.erb +41 -0
- data/lib/tumblr/views/success.erb +6 -0
- data/man/tumblr.1 +67 -65
- data/man/tumblr.1.html +131 -108
- data/man/tumblr.1.ronn +76 -57
- data/man/tumblr.5 +48 -68
- data/man/tumblr.5.html +106 -114
- data/man/tumblr.5.ronn +38 -51
- data/spec/fixtures/posts.json +10 -0
- data/spec/fixtures/typical_animated_gif.gif +0 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/tumblr/authentication_spec.rb +57 -0
- data/spec/tumblr/client_spec.rb +223 -0
- data/spec/tumblr/credentials_spec.rb +63 -0
- data/spec/tumblr/post_spec.rb +125 -0
- data/tumblr-rb.gemspec +16 -89
- metadata +101 -102
- data/lib/tumblr/authenticator.rb +0 -18
- data/lib/tumblr/post/conversation.rb +0 -15
- data/lib/tumblr/post/regular.rb +0 -14
- data/lib/tumblr/reader.rb +0 -191
- data/lib/tumblr/writer.rb +0 -39
- data/test/fixtures/vcr_cassettes/authenticate/authenticate.yml +0 -39
- data/test/fixtures/vcr_cassettes/read/all_pages.yml +0 -34
- data/test/fixtures/vcr_cassettes/read/authenticated.yml +0 -40
- data/test/fixtures/vcr_cassettes/read/authentication_failure.yml +0 -33
- data/test/fixtures/vcr_cassettes/read/like.yml +0 -31
- data/test/fixtures/vcr_cassettes/read/mwunsch.yml +0 -101
- data/test/fixtures/vcr_cassettes/read/optional.yml +0 -48
- data/test/fixtures/vcr_cassettes/read/pages.yml +0 -36
- data/test/fixtures/vcr_cassettes/read/tumblrgemtest.yml +0 -42
- data/test/fixtures/vcr_cassettes/read/unlike.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/delete.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/edit.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/reblog.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/write.yml +0 -31
- data/test/helper.rb +0 -44
- data/test/test_tumblr.rb +0 -710
data/lib/tumblr.rb
CHANGED
|
@@ -1,185 +1,8 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'tumblr/version'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
require 'tumblr/reader'
|
|
10
|
-
require 'tumblr/writer'
|
|
11
|
-
require 'tumblr/authenticator'
|
|
12
|
-
|
|
13
|
-
def initialize(*credentials)
|
|
14
|
-
@credentials = {:email => credentials[0], :password => credentials[1]} unless credentials.blank?
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Convenience method for Reader#read
|
|
18
|
-
def read(username, parameters={})
|
|
19
|
-
reader.read(username, parameters)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Post a document to Tumblr. If the document has a post-id, it will be edited.
|
|
23
|
-
def post(doc)
|
|
24
|
-
tumblr_post = if doc.is_a?(Tumblr::Post)
|
|
25
|
-
doc.to_h
|
|
26
|
-
elsif doc.respond_to?(:keys)
|
|
27
|
-
doc
|
|
28
|
-
else
|
|
29
|
-
Tumblr.parse(doc).to_h
|
|
30
|
-
end
|
|
31
|
-
tumblr_post.has_key?(:'post-id') ? writer.edit(tumblr_post) : writer.write(tumblr_post)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def dashboard(parameters={})
|
|
35
|
-
raise 'Requires an e-mail address and password' unless @credentials
|
|
36
|
-
reader.dashboard(parameters)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def authenticate(theme = false)
|
|
40
|
-
raise 'Requires an e-mail address and password' unless @credentials
|
|
41
|
-
params = theme ? {:'include-theme' => 1} : {}
|
|
42
|
-
Authenticator.new(@credentials[:email],@credentials[:password]).authenticate(params)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def pages(username)
|
|
46
|
-
reader.pages(username)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def all_pages(username)
|
|
50
|
-
reader.all_pages(username)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def reader
|
|
54
|
-
if @credentials.blank?
|
|
55
|
-
Reader.new
|
|
56
|
-
else
|
|
57
|
-
Reader.new(@credentials[:email],@credentials[:password])
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def writer
|
|
62
|
-
raise 'Requires an e-mail address and password' unless @credentials
|
|
63
|
-
Writer.new(@credentials[:email],@credentials[:password])
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def self.execute(credentials, input)
|
|
67
|
-
request = new(credentials[:email],credentials[:password]).post(input)
|
|
68
|
-
request.perform
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Parse a post out of a string
|
|
72
|
-
def self.parse(doc)
|
|
73
|
-
document = {}
|
|
74
|
-
if doc =~ /^(\s*---(.*)---\s*)/m
|
|
75
|
-
document[:data] = YAML.load(Regexp.last_match[2].strip)
|
|
76
|
-
document[:body] = doc.sub(Regexp.last_match[1],'').strip
|
|
77
|
-
else
|
|
78
|
-
document[:data] = {'type' => infer_post_type(doc)}
|
|
79
|
-
document[:body] = doc
|
|
80
|
-
end
|
|
81
|
-
create_post document
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Guess the Type of Post for a given documents
|
|
85
|
-
def self.infer_post_type(doc)
|
|
86
|
-
begin
|
|
87
|
-
url = URI.parse(doc)
|
|
88
|
-
if url.is_a?(URI::HTTP)
|
|
89
|
-
(url.host.include?('youtube.com') || url.host.include?('vimeo.com')) ? :video : :link
|
|
90
|
-
else
|
|
91
|
-
:regular
|
|
92
|
-
end
|
|
93
|
-
rescue URI::InvalidURIError
|
|
94
|
-
:regular
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Map a post type key to its class
|
|
99
|
-
def self.map(key)
|
|
100
|
-
case key
|
|
101
|
-
when :regular
|
|
102
|
-
Post::Regular
|
|
103
|
-
when :photo
|
|
104
|
-
Post::Photo
|
|
105
|
-
when :quote
|
|
106
|
-
Post::Quote
|
|
107
|
-
when :link
|
|
108
|
-
Post::Link
|
|
109
|
-
when :conversation
|
|
110
|
-
Post::Conversation
|
|
111
|
-
when :video
|
|
112
|
-
Post::Video
|
|
113
|
-
when :audio
|
|
114
|
-
Post::Audio
|
|
115
|
-
else
|
|
116
|
-
raise "#{key} is not an understood Tumblr post type"
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
private
|
|
121
|
-
|
|
122
|
-
def self.create_post(document)
|
|
123
|
-
post_data = document[:data]
|
|
124
|
-
type = (post_data['type'] || infer_post_type(document[:body])).to_sym
|
|
125
|
-
post = case type
|
|
126
|
-
when :regular
|
|
127
|
-
create_regular document
|
|
128
|
-
when :photo
|
|
129
|
-
create_photo document
|
|
130
|
-
when :audio
|
|
131
|
-
create_audio document
|
|
132
|
-
when :quote, :link, :conversation, :video
|
|
133
|
-
create_simple type, document
|
|
134
|
-
else
|
|
135
|
-
raise "#{type} is not a recognized Tumblr post type."
|
|
136
|
-
end
|
|
137
|
-
basic_setup post, post_data
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def self.basic_setup(post, post_data)
|
|
141
|
-
%w(format state private slug date group generator reblog-key).each do |basic|
|
|
142
|
-
post.send "#{basic}=".gsub('-','_').intern, post_data[basic] if post_data[basic]
|
|
143
|
-
end
|
|
144
|
-
%w(tags send-to-twitter publish-on).each do |attribute|
|
|
145
|
-
post.send attribute.gsub('-','_').intern, post_data[attribute] if post_data[attribute]
|
|
146
|
-
end
|
|
147
|
-
post
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def self.setup_params(post, data)
|
|
151
|
-
post_params = post.class.parameters.collect {|param| param.to_s.gsub('_','-') }
|
|
152
|
-
post_params.each do |param|
|
|
153
|
-
post.send "#{param.gsub('-','_')}=".intern, data[param] if data[param]
|
|
154
|
-
end
|
|
155
|
-
post
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def self.create_regular(doc)
|
|
159
|
-
data = doc[:data]
|
|
160
|
-
post = Tumblr::Post::Regular.new(data['post-id'])
|
|
161
|
-
post.body = doc[:body]
|
|
162
|
-
setup_params post, data
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def self.create_photo(doc)
|
|
166
|
-
data = doc[:data]
|
|
167
|
-
post = Tumblr::Post::Photo.new(data['post-id'])
|
|
168
|
-
post.source = doc[:body]
|
|
169
|
-
setup_params post, data
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def self.create_audio(doc)
|
|
173
|
-
data = doc[:data]
|
|
174
|
-
post = Tumblr::Post::Audio.new(data['post-id'])
|
|
175
|
-
post.externally_hosted_url = doc[:body]
|
|
176
|
-
setup_params post, data
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def self.create_simple(type, doc)
|
|
180
|
-
data = doc[:data]
|
|
181
|
-
post = map(type).new(doc[:body], data['post-id'])
|
|
182
|
-
setup_params post, data
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
end
|
|
3
|
+
module Tumblr
|
|
4
|
+
require "tumblr/client"
|
|
5
|
+
require "tumblr/post"
|
|
6
|
+
|
|
7
|
+
autoload :Authentication, "tumblr/authentication"
|
|
8
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'sinatra/base'
|
|
2
|
+
require 'weary/request'
|
|
3
|
+
require 'weary/middleware'
|
|
4
|
+
|
|
5
|
+
module Tumblr
|
|
6
|
+
# http://www.tumblr.com/oauth/apps
|
|
7
|
+
class Authentication < Sinatra::Base
|
|
8
|
+
HOST = "http://www.tumblr.com/oauth"
|
|
9
|
+
|
|
10
|
+
enable :sessions
|
|
11
|
+
set :credential_path, nil
|
|
12
|
+
|
|
13
|
+
def request_token(key, secret, callback)
|
|
14
|
+
Weary::Request.new "#{HOST}/request_token", :POST do |req|
|
|
15
|
+
req.params :oauth_callback => callback
|
|
16
|
+
req.use Weary::Middleware::OAuth, :consumer_key => key,
|
|
17
|
+
:consumer_secret => secret
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def access_token(token, token_secret, verifier, consumer_key, consumer_secret)
|
|
22
|
+
Weary::Request.new "#{HOST}/access_token", :POST do |req|
|
|
23
|
+
req.use Weary::Middleware::OAuth, :token => token,
|
|
24
|
+
:token_secret => token_secret,
|
|
25
|
+
:verifier => verifier,
|
|
26
|
+
:consumer_key => consumer_key,
|
|
27
|
+
:consumer_secret => consumer_secret
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
get "/" do
|
|
32
|
+
halt 400, erb(:form) unless params["key"] && params["secret"]
|
|
33
|
+
session[:consumer_key] = params["key"]
|
|
34
|
+
session[:consumer_secret] = params["secret"]
|
|
35
|
+
response = request_token(session[:consumer_key], session[:consumer_secret], url("/auth")).perform
|
|
36
|
+
if response.success?
|
|
37
|
+
result = Rack::Utils.parse_query(response.body)
|
|
38
|
+
logger.info(request.host)
|
|
39
|
+
session[:request_token_secret] = result["oauth_token_secret"]
|
|
40
|
+
redirect to("#{HOST}/authorize?oauth_token=#{result['oauth_token']}")
|
|
41
|
+
else
|
|
42
|
+
status response.status
|
|
43
|
+
erb response.body
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
get "/auth" do
|
|
48
|
+
halt 401, erb(:error) if params.empty?
|
|
49
|
+
token = params["oauth_token"]
|
|
50
|
+
verifier = params["oauth_verifier"]
|
|
51
|
+
response = access_token(token, session[:request_token_secret], verifier,
|
|
52
|
+
session[:consumer_key], session[:consumer_secret]).perform
|
|
53
|
+
if response.success?
|
|
54
|
+
require 'tumblr/credentials'
|
|
55
|
+
result = Rack::Utils.parse_query(response.body)
|
|
56
|
+
credentials = Tumblr::Credentials.new(settings.credential_path)
|
|
57
|
+
credentials.write session[:consumer_key],
|
|
58
|
+
session[:consumer_secret],
|
|
59
|
+
result["oauth_token"],
|
|
60
|
+
result["oauth_token_secret"]
|
|
61
|
+
@credential_path = credentials.path
|
|
62
|
+
status response.status
|
|
63
|
+
erb :success
|
|
64
|
+
else
|
|
65
|
+
status response.status
|
|
66
|
+
erb response.body
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require "weary"
|
|
2
|
+
|
|
3
|
+
module Tumblr
|
|
4
|
+
class Client < Weary::Client
|
|
5
|
+
API_VERSION = "v2"
|
|
6
|
+
|
|
7
|
+
USER_AGENT = "Tumblr API Client (Ruby)/#{Tumblr::VERSION} (+http://github.com/mwunsch/tumblr)"
|
|
8
|
+
|
|
9
|
+
POST_OPTIONS = [
|
|
10
|
+
:state, :tags, :tweet, :date, :format, :slug,
|
|
11
|
+
:title, :body, # Text posts
|
|
12
|
+
:caption, :link, :source, :data, #Photo posts
|
|
13
|
+
:quote, # Quote posts
|
|
14
|
+
:url, :description, # Link posts
|
|
15
|
+
:conversation, # Chat posts
|
|
16
|
+
:external_url, # Audio posts
|
|
17
|
+
:embed, # Video posts
|
|
18
|
+
:answer # Answer posts ??
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
domain "http://api.tumblr.com/#{API_VERSION}"
|
|
22
|
+
|
|
23
|
+
user_agent USER_AGENT
|
|
24
|
+
|
|
25
|
+
get :info, "/blog/{hostname}/info" do |r|
|
|
26
|
+
r.required :api_key
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
get :avatar, "/blog/{hostname}/avatar" do |r|
|
|
30
|
+
r.optional :size
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
get :followers, "/blog/{hostname}/followers" do |r|
|
|
34
|
+
r.oauth!
|
|
35
|
+
r.optional :limit, :offset
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
get :posts, "/blog/{hostname}/posts" do |r|
|
|
39
|
+
r.required :api_key
|
|
40
|
+
r.optional :type, :id, :tag, :limit, :offset, :reblog_info,
|
|
41
|
+
:notes_info, :filter
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
get :queue, "/blog/{hostname}/posts/queue" do |r|
|
|
45
|
+
r.oauth!
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
get :draft, "/blog/{hostname}/posts/draft" do |r|
|
|
49
|
+
r.oauth!
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
get :submission, "/blog/{hostname}/posts/submission" do |r|
|
|
53
|
+
r.oauth!
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
post :post, "/blog/{hostname}/post" do |r|
|
|
57
|
+
r.oauth!
|
|
58
|
+
r.required :type
|
|
59
|
+
r.optional *POST_OPTIONS
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
post :edit, "/blog/{hostname}/post/edit" do |r|
|
|
63
|
+
r.oauth!
|
|
64
|
+
r.required :id
|
|
65
|
+
r.optional *POST_OPTIONS
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
post :reblog, "/blog/{hostname}/post/reblog" do |r|
|
|
69
|
+
r.oauth!
|
|
70
|
+
r.required :id, :reblog_key
|
|
71
|
+
r.optional *(POST_OPTIONS | [:comment])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
post :delete, "/blog/{hostname}/post/delete" do |r|
|
|
75
|
+
r.oauth!
|
|
76
|
+
r.required :id
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
get :user, "/user/info" do |r|
|
|
80
|
+
r.oauth!
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
get :dashboard, "/user/dashboard" do |r|
|
|
84
|
+
r.oauth!
|
|
85
|
+
r.optional :limit, :offset, :type, :since_id, :reblog_info, :notes_info
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
get :likes, "/user/likes" do |r|
|
|
89
|
+
r.oauth!
|
|
90
|
+
r.optional :limit, :offset
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
get :following, "/user/following" do |r|
|
|
94
|
+
r.oauth!
|
|
95
|
+
r.optional :limit, :offset
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
post :follow, "/user/follow" do |r|
|
|
99
|
+
r.oauth!
|
|
100
|
+
r.required :url
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
post :unfollow, "/user/unfollow" do |r|
|
|
104
|
+
r.oauth!
|
|
105
|
+
r.required :url
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
post :like, "/user/like" do |r|
|
|
109
|
+
r.oauth!
|
|
110
|
+
r.required :id, :reblog_key
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
post :unlike, "/user/unlike" do |r|
|
|
114
|
+
r.oauth!
|
|
115
|
+
r.required :id, :reblog_key
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
get :tagged, "/tagged" do |r|
|
|
119
|
+
r.required :api_key, :tag
|
|
120
|
+
r.optional :before, :limit, :filter
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.load(hostname = nil, path = nil)
|
|
124
|
+
require "tumblr/credentials"
|
|
125
|
+
credentials = Tumblr::Credentials.new(path).read
|
|
126
|
+
self.new(hostname, credentials)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def initialize(hostname = nil, oauth_params = {})
|
|
130
|
+
@defaults = {}
|
|
131
|
+
@defaults[:hostname] = hostname if hostname
|
|
132
|
+
[:consumer_key, :consumer_secret, :token, :token_secret].each do |param|
|
|
133
|
+
@defaults[param] = value_for_key_as_string_or_symbol(oauth_params, param) if hash_includes_key_as_string_or_symbol?(oauth_params, param)
|
|
134
|
+
end
|
|
135
|
+
@defaults[:api_key] = value_for_key_as_string_or_symbol(oauth_params, :consumer_key)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def value_for_key_as_string_or_symbol(hash, key)
|
|
141
|
+
hash[key.to_sym] || hash[key.to_s]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def hash_includes_key_as_string_or_symbol?(hash, key)
|
|
145
|
+
hash.keys.map(&:to_s).include? key.to_s
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'tumblr'
|
|
3
|
+
|
|
4
|
+
module Tumblr
|
|
5
|
+
class CommandLineInterface < Thor
|
|
6
|
+
include Thor::Actions
|
|
7
|
+
|
|
8
|
+
default_task :pipe
|
|
9
|
+
|
|
10
|
+
class_option :credentials, :type => :string,
|
|
11
|
+
:desc => "The file path where your Tumblr OAuth keys are stored. Defaults to ~/.tumblr."
|
|
12
|
+
class_option :host, :type => :string,
|
|
13
|
+
:desc => 'The hostname of the blog you want to post to i.e. "YOUR-NAME.tumblr.com"'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
check_unknown_options!(:except => [])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
desc "pipe", "Pipe post content in from STDIN"
|
|
20
|
+
method_option :publish, :type => :boolean,
|
|
21
|
+
:aliases => "-p",
|
|
22
|
+
:desc => "Publish this post"
|
|
23
|
+
method_option :queue, :type => :boolean,
|
|
24
|
+
:aliases => "-q",
|
|
25
|
+
:desc => "Add this post to the queue"
|
|
26
|
+
method_option :draft, :type => :boolean,
|
|
27
|
+
:aliases => "-d",
|
|
28
|
+
:desc => "Save this post as a draft"
|
|
29
|
+
long_desc <<-LONGDESC
|
|
30
|
+
Publish a post to tumblr from STDIN for HOST.
|
|
31
|
+
It is assumed that STDIN contains a document formatted according to `tumblr(5)`.
|
|
32
|
+
If STDIN contains a URL, it will create a post using the same rules as `tumblr post`.
|
|
33
|
+
|
|
34
|
+
Writes the serialized post to STDOUT.
|
|
35
|
+
LONGDESC
|
|
36
|
+
def pipe
|
|
37
|
+
if !$stdin.tty?
|
|
38
|
+
puts post($stdin).serialize
|
|
39
|
+
else
|
|
40
|
+
invoke :help
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc "post <POST> | <FILE> | <URL>", "Posts to tumblr"
|
|
45
|
+
method_option :publish, :type => :boolean,
|
|
46
|
+
:aliases => "-p",
|
|
47
|
+
:desc => "Publish this post"
|
|
48
|
+
method_option :queue, :type => :boolean,
|
|
49
|
+
:aliases => "-q",
|
|
50
|
+
:desc => "Add this post to the queue"
|
|
51
|
+
method_option :draft, :type => :boolean,
|
|
52
|
+
:aliases => "-d",
|
|
53
|
+
:desc => "Save this post as a draft"
|
|
54
|
+
long_desc <<-LONGDESC
|
|
55
|
+
Post a POST to Tumblr for HOST. If a FILE path is given, the file will be read and posted to Tumblr.
|
|
56
|
+
It is assumed the post follows the `tumblr(5)` format.
|
|
57
|
+
|
|
58
|
+
If the FILE is an audio, image, or video file, it will create the respective post type on tumblr.
|
|
59
|
+
|
|
60
|
+
If a URL is given, a link post will be created.
|
|
61
|
+
If URL is a YouTube or Vimeo link, it will create a video post.
|
|
62
|
+
If URL is a SoundCloud or Spotify link, an audio post will be published.
|
|
63
|
+
LONGDESC
|
|
64
|
+
def post(arg)
|
|
65
|
+
client = get_client
|
|
66
|
+
post = if arg.respond_to? :read
|
|
67
|
+
Tumblr::Post.load arg.read
|
|
68
|
+
elsif File.file?(file_path = File.expand_path(arg))
|
|
69
|
+
Tumblr::Post.load_from_path file_path
|
|
70
|
+
else
|
|
71
|
+
Tumblr::Post.load arg.to_s
|
|
72
|
+
end
|
|
73
|
+
post.publish! if options[:publish]
|
|
74
|
+
post.queue! if options[:queue]
|
|
75
|
+
post.draft! if options[:draft]
|
|
76
|
+
response = post.post(client).perform
|
|
77
|
+
tumblr_error(response) unless response.success?
|
|
78
|
+
ui_success %Q(Post was successfully created! Post ID: #{response.parse["response"]["id"]}) if $stdin.tty?
|
|
79
|
+
post
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
desc "edit POST_ID", "Edit a post"
|
|
83
|
+
long_desc "Open up your $EDITOR to edit a published post."
|
|
84
|
+
long_desc <<-LONGDESC
|
|
85
|
+
Get a post from Tumblr and edit it.
|
|
86
|
+
|
|
87
|
+
Behaves similar to `git commit`, in that it will open up your editor in the foreground.
|
|
88
|
+
Look for a $TUMBLREDITOR environment variable, and if that's not found, will use $EDITOR.
|
|
89
|
+
LONGDESC
|
|
90
|
+
def edit(id)
|
|
91
|
+
client = get_client
|
|
92
|
+
response = client.posts(:id => id, :filter => :raw).perform
|
|
93
|
+
tumblr_error(response) unless response.success?
|
|
94
|
+
post = Tumblr::Post.create(response.parse["response"]["posts"].first)
|
|
95
|
+
require 'tempfile'
|
|
96
|
+
tmp_file = Tempfile.new("post_#{id}")
|
|
97
|
+
tmp_file.write(post.serialize)
|
|
98
|
+
tmp_file.rewind
|
|
99
|
+
ui_abort "Something went wrong editing the post." unless system "#{editor} #{tmp_file.path}"
|
|
100
|
+
edited_post = Tumblr::Post.load_from_path tmp_file.path
|
|
101
|
+
edited_response = edited_post.edit(client).perform
|
|
102
|
+
tumblr_error(edited_response) unless edited_response.success?
|
|
103
|
+
ui_success "Post #{id} successfully edited."
|
|
104
|
+
ensure
|
|
105
|
+
if tmp_file
|
|
106
|
+
tmp_file.close
|
|
107
|
+
tmp_file.unlink
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
desc "fetch POST_ID", "Fetch a post and write out its serialized form."
|
|
112
|
+
def fetch(id)
|
|
113
|
+
client = get_client
|
|
114
|
+
response = client.posts(:id => id, :filter => :raw).perform
|
|
115
|
+
tumblr_error(response) unless response.success?
|
|
116
|
+
post = Tumblr::Post.create(response.parse["response"]["posts"].first)
|
|
117
|
+
puts post.serialize
|
|
118
|
+
post
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
desc "delete POST_ID", "Delete a post"
|
|
122
|
+
def delete(id)
|
|
123
|
+
client = get_client
|
|
124
|
+
response = client.delete(:id => id).perform
|
|
125
|
+
tumblr_error(response) unless response.success?
|
|
126
|
+
ui_success "Post #{id} successfully deleted."
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
desc "authorize", "Authenticate and authorize the cli to post on your behalf"
|
|
130
|
+
option :port, :type => :string,
|
|
131
|
+
:default => "4567",
|
|
132
|
+
:desc => "Use PORT"
|
|
133
|
+
option :bind, :type => :string,
|
|
134
|
+
:default => "0.0.0.0",
|
|
135
|
+
:desc => "listen on BIND"
|
|
136
|
+
long_desc <<-LONGDESC
|
|
137
|
+
`tumblr authorize` will start up a server (listening on BIND and PORT) running
|
|
138
|
+
a small app to do the OAuth handshake with tumblr.
|
|
139
|
+
|
|
140
|
+
The app will open in the default browser, allowing you to authenticate to Tumblr
|
|
141
|
+
and authorize `tumblr` to do actions on your behalf. You will need to register
|
|
142
|
+
an application and enter the consumer key and consumer secret. The application will
|
|
143
|
+
write the OAuth keys to your CREDENTIALS file.
|
|
144
|
+
|
|
145
|
+
After authorization, you will be prompted to return to your terminal and shut down
|
|
146
|
+
the server (using CTRL-C).
|
|
147
|
+
LONGDESC
|
|
148
|
+
def authorize(*soak)
|
|
149
|
+
require 'tumblr/authentication'
|
|
150
|
+
sinatra_options = {
|
|
151
|
+
:port => options[:port],
|
|
152
|
+
:bind => options[:bind],
|
|
153
|
+
:credential_path => options[:credentials]
|
|
154
|
+
}
|
|
155
|
+
Tumblr::Authentication.run!(sinatra_options) do |server|
|
|
156
|
+
`open http://#{options[:bind]}:#{options[:port]}/`
|
|
157
|
+
end
|
|
158
|
+
if has_credentials?
|
|
159
|
+
ui_success "Success! Your Tumblr OAuth credentials were written to #{credentials.path}"
|
|
160
|
+
else
|
|
161
|
+
ui_abort "Something went wrong in authorization, and credentials were not correctly written to #{credentials.path}"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
desc "version", "Print Tumblr version information"
|
|
166
|
+
def version
|
|
167
|
+
puts Tumblr::VERSION
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
def credentials
|
|
173
|
+
require 'tumblr/credentials'
|
|
174
|
+
Tumblr::Credentials.new(options[:credentials] || ENV["TUMBLRCRED"])
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def editor
|
|
178
|
+
ENV["TUMBLREDITOR"] || ENV["EDITOR"]
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def has_credentials?
|
|
182
|
+
!credentials.read.empty?
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def check_credentials
|
|
186
|
+
ui_abort "Unable to find your OAuth keys. Run `tumblr authorize` to authenticate with Tumblr." unless has_credentials?
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def get_host
|
|
190
|
+
host = ask("What is your Tumblr hostname?") if options[:host].nil? and $stdin.tty?
|
|
191
|
+
host ||= options[:host]
|
|
192
|
+
ui_abort "You need to provide a hostname i.e. --host=YOUR-NAME.tumblr.com" if host.nil? or host.empty?
|
|
193
|
+
host
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def get_client
|
|
197
|
+
check_credentials
|
|
198
|
+
host = get_host
|
|
199
|
+
Tumblr::Client.load host, options[:credentials]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def tumblr_error(response)
|
|
203
|
+
parsed_response = response.parse
|
|
204
|
+
msg = parsed_response["response"].empty? ? response.parse["meta"]["msg"] : parsed_response["response"]["errors"]
|
|
205
|
+
ui_abort %Q(Tumblr returned a #{response.status} Error: #{msg})
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def ui_abort(msg, exit_status = 1)
|
|
209
|
+
ui_puts msg, :red
|
|
210
|
+
exit exit_status
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def ui_success(msg)
|
|
214
|
+
ui_puts msg, :green
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def ui_puts(msg, color = nil)
|
|
218
|
+
say msg, $stdout.tty? ? color : nil
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
end
|
|
222
|
+
end
|