Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
233 lines (195 sloc) 8.24 KB
require 'sinatra'
Bundler.require(:default)
require 'settings'
enable :sessions
use Rack::Session::Cookie, :key => 'telepaper.session',
:path => '/',
:expire_after => 2592000, # In seconds
:secret => settings.session_secret
set :haml, :format => :html5
before do
@readability_client ||= OAuth::Consumer.new(settings.readability_api_key, settings.readability_api_secret,
:site => 'https://www.readability.com',
:request_token_path => '/api/rest/v1/oauth/request_token',
:access_token_path => '/api/rest/v1/oauth/access_token',
:authorize_path => '/api/rest/v1/oauth/authorize'
)
session[:readability] ||= {}
if !session[:readability][:request_token].nil? && !session[:readability][:request_token_secret].nil?
@readability_request_token = OAuth::RequestToken.new(@readability_client, session[:readability][:request_token], session[:readability][:request_token_secret])
end
if !session[:readability][:access_token].nil? && !session[:readability][:access_token_secret].nil?
@readability_access_token = OAuth::AccessToken.new(@readability_client, session[:readability][:access_token], session[:readability][:access_token_secret])
end
session[:newspaperclub] ||= {}
@newspaperclub_client ||= OAuth2::Client.new(settings.newspaperclub_api_key, settings.newspaperclub_api_secret,
:site => 'https://api.newspaperclub.com/v1',
:access_token_method => :post,
:parse_json => true,
:ssl => { :verify_mode => OpenSSL::SSL::VERIFY_PEER, :ca_path => settings.ca_path },
:token_url => 'https://www.newspaperclub.com/oauth/access_token',
:authorize_url => 'https://www.newspaperclub.com/oauth/authorize'
)
if !session[:newspaperclub][:access_token].nil?
@newspaperclub_access_token = OAuth2::AccessToken.new(@newspaperclub_client, session[:newspaperclub][:access_token],
:header_format => 'OAuth %s',
:mode => :header
)
end
end
get '/auth/readability' do
@readability_request_token = @readability_client.get_request_token(:oauth_callback => readability_redirect_uri)
session[:readability][:request_token] = @readability_request_token.token
session[:readability][:request_token_secret] = @readability_request_token.secret
redirect @readability_request_token.authorize_url
end
get '/auth/readability/callback' do
@readability_access_token = @readability_request_token.get_access_token :oauth_verifier => params[:oauth_verifier]
session[:readability][:access_token] = @readability_access_token.token
session[:readability][:access_token_secret] = @readability_access_token.secret
redirect '/'
end
get '/auth/newspaperclub' do
redirect @newspaperclub_client.auth_code.authorize_url(:redirect_uri => newspaperclub_redirect_uri)
end
get '/auth/newspaperclub/callback' do
access_token = @newspaperclub_client.auth_code.get_token(params['code'], :redirect_uri => newspaperclub_redirect_uri)
session[:newspaperclub][:access_token] = access_token.token
redirect '/'
end
get '/' do
if @readability_access_token && @newspaperclub_access_token
@bookmarks = JSON.parse(@readability_access_token.get('/api/rest/v1/bookmarks?archive=0').body)
@newspapers = @newspaperclub_access_token.get('/v1/newspapers.json').parsed.select { |n| n['newspaper']['type'] == 'arthr' }
haml :index
else
haml :logged_out
end
end
post '/generate' do
# take each of the articles passed in, and run it through the HTML to markdown
# converter
@articles = params[:articles].map do |article_id|
JSON.parse(@readability_access_token.get("/api/rest/v1/articles/#{article_id}").body).tap do |article|
markdown_reply = @newspaperclub_access_token.post('/v1/utils/html_to_markdown',
:body => { :html => article['content'] }.to_json,
:headers => { 'Content-Type' => 'application/json'}
)
article['markdown_content'] = markdown_reply.parsed['markdown']
end
end
# TODO: use the timestamps of bookmarking, not the generation time.
subtitle = "Your Readability bookmarks, generated on #{Date.today.strftime('%d').to_i.ordinalize} #{Date.today.strftime('%B')}"
# if we have 0 for the newspaper_id, create a new one, otherwise grab an
# existing one.
if params[:newspaper_id].to_i == 0
# Now create the newspaper. Notice auto_page_count is true, which will resize
# the paper to fit all the stories in it.
request = { :newspaper => {
:title => "The Telepaper",
:subtitle => subtitle,
:printing_target => 'web_offset', # target web_offset printing, for now.
:font_size => 'small', # TODO: make this an option
:auto_page_count => true }
}
response = @newspaperclub_access_token.post('/v1/newspapers',
:body => request.to_json,
:headers => { 'Content-Type' => 'application/json'}
)
else
# set auto_page_count on, so it'll expand the newspaper if necessary.
response = @newspaperclub_access_token.put("/v1/newspapers/#{params[:newspaper_id]}",
:body => { :newspaper => { :auto_page_count => true } }.to_json,
:headers => { 'Content-Type' => 'application/json' }
)
end
# we use the ID to refer to the created newspaper later
@newspaper_id = response.parsed['newspaper']['id']
# we use the secret later for displaying the front page preview
@newspaper_secret = response.parsed['newspaper']['secret']
# Now we go and insert each story into the newspaper, in order. We're using
# the markdown format for rich-text formatting. This is still a bit
# experimental.
@articles.each do |article|
article_request = {
:story => {
:markup => 'markdown',
:headline => article['title'],
:body => article['markdown_content'],
:byline => article['url']
}
}
@newspaperclub_access_token.post("/v1/newspapers/#{@newspaper_id}/stories",
:body => article_request.to_json,
:headers => { 'Content-Type' => 'application/json'}
)
end
# rebuild the paper
build_newspaper(@newspaper_id)
# check whether any of the stories have overflowed the end of the paper, and
# remove them from the newspaper.
# because it's set to auto_page_count, this should only happen if we exceed
# 64 pages.
stories_response = @newspaperclub_access_token.get("/v1/newspapers/#{@newspaper_id}/stories",
:headers => { 'Content-Type' => 'application/json' }
)
overflowed_stories = stories_response.parsed.select do |story|
story['story']['end_overflows']
end
if overflowed_stories.any?
overflowed_stories.each do |story|
@newspaperclub_access_token.delete("/v1/newspapers/#{@newspaper_id}/stories/#{story['story']['id']}",
:headers => { 'Content-Type' => 'application/json' }
)
end
build_newspaper(@newspaper_id) # rebuild one last time
end
# we disable auto page count after we've finished the newspaper, because
# there's no way of disabling it in ARTHR yet, and it'll confuse people.
@newspaperclub_access_token.put("/v1/newspapers/#{@newspaper_id}",
:headers => { 'Content-Type' => 'application/json' },
:body => { :newspaper => { :auto_page_count => false } }.to_json
)
haml :generated
end
get '/sign_out' do
session.clear
redirect '/'
end
private
def newspaperclub_redirect_uri
uri = URI.parse(request.url)
uri.path = '/auth/newspaperclub/callback'
uri.query = nil
uri.to_s
end
def readability_redirect_uri
uri = URI.parse(request.url)
uri.path = '/auth/readability/callback'
uri.query = nil
uri.to_s
end
# builds the newspaper with the id you passed in, and then polls every second
# for completion of the build.
def build_newspaper(newspaper_id)
build_response = @newspaperclub_access_token.post("/v1/newspapers/#{newspaper_id}/build", :headers => { 'Content-Type' => 'application/json' })
job_id = build_response.parsed['job_id']
start_time = Time.now
loop do
job_response = @newspaperclub_access_token.get("/v1/jobs/#{job_id}", :headers => { 'Content-Type' => 'application/json' })
case job_response.parsed['job']['status']
when 'completed'
break
when 'failed'
raise "Job Failed"
when 'working', 'queued'
if Time.now - start_time >= 30
raise "Timeout"
else
sleep 1
end
else
raise "Unknown State" # we should never hit this
end
end
end