From 172a85ae1470930b8a241dd02b988f62a303b7d3 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 11 Oct 2013 11:46:20 +1100 Subject: [PATCH] prettier twitter links, extracted mini api --- lib/oneboxer/twitter_onebox.rb | 101 +------------- lib/twitter_api.rb | 123 ++++++++++++++++++ .../oneboxer/twitter_onebox_spec.rb | 2 +- 3 files changed, 129 insertions(+), 97 deletions(-) create mode 100644 lib/twitter_api.rb diff --git a/lib/oneboxer/twitter_onebox.rb b/lib/oneboxer/twitter_onebox.rb index a0a292e6a..aef467a58 100644 --- a/lib/oneboxer/twitter_onebox.rb +++ b/lib/oneboxer/twitter_onebox.rb @@ -1,4 +1,5 @@ require_dependency 'oneboxer/handlebars_onebox' +require_dependency 'twitter_api' module Oneboxer class TwitterOnebox < HandlebarsOnebox @@ -17,9 +18,10 @@ module Oneboxer favicon 'twitter.png' def fetch_html - raise Discourse::SiteSettingMissing if twitter_credentials_missing? + raise Discourse::SiteSettingMissing if TwitterApi.twitter_credentials_missing? - tweet_for @url.match(REGEX)[:id] + # a bit odd, but I think the api expects html + TwitterApi.raw_tweet_for(@url.match(REGEX)[:id]) end def parse(data) @@ -28,103 +30,10 @@ module Oneboxer result['created_at'] = Time.parse(result['created_at']).strftime("%I:%M%p - %d %b %y") - result['text'] = link_all_the_things_in result['text'] + result['text'] = TwitterApi.prettify_tweet(result) result end - private - - def link_all_the_things_in(text) - link_hashtags_in link_handles_in link_urls_in(text) - end - - def link_urls_in(text) - URI.extract(text, %w(http https)).each do |url| - text.gsub!(url, "#{url}") - end - - text - end - - def link_handles_in(text) - text.scan(/\s@(\w+)/).flatten.uniq.each do |handle| - text.gsub!("@#{handle}", [ - "", - "@#{handle}", - "" - ].join) - end - - text - end - - def link_hashtags_in(text) - text.scan(/\s#(\w+)/).flatten.uniq.each do |hashtag| - text.gsub!("##{hashtag}", [ - "", - "##{hashtag}", - "" - ].join) - end - - text - end - - def tweet_for(id) - request = Net::HTTP::Get.new(tweet_uri_for id) - - request.add_field 'Authorization', "Bearer #{bearer_token}" - - http(tweet_uri_for id).request(request).body - end - - def authorization - request = Net::HTTP::Post.new(auth_uri) - - request.add_field 'Authorization', - "Basic #{bearer_token_credentials}" - request.add_field 'Content-Type', - 'application/x-www-form-urlencoded;charset=UTF-8' - - request.set_form_data 'grant_type' => 'client_credentials' - - http(auth_uri).request(request).body - end - - def bearer_token - @access_token ||= JSON.parse(authorization).fetch('access_token') - end - - def bearer_token_credentials - Base64.strict_encode64( - "#{URI::encode(consumer_key)}:#{URI::encode(consumer_secret)}" - ) - end - - def auth_uri - URI.parse "#{BASE_URL}/oauth2/token" - end - - def tweet_uri_for(id) - URI.parse "#{BASE_URL}/1.1/statuses/show.json?id=#{id}" - end - - def http(uri) - Net::HTTP.new(uri.host, uri.port).tap { |http| http.use_ssl = true } - end - - def consumer_key - SiteSetting.twitter_consumer_key - end - - def consumer_secret - SiteSetting.twitter_consumer_secret - end - - def twitter_credentials_missing? - consumer_key.blank? || consumer_secret.blank? - end end end diff --git a/lib/twitter_api.rb b/lib/twitter_api.rb new file mode 100644 index 000000000..8b5cac53a --- /dev/null +++ b/lib/twitter_api.rb @@ -0,0 +1,123 @@ +# lightweight Twitter api calls +require_dependency 'rinku' + +class TwitterApi + + class << self + + def prettify_tweet(tweet) + text = tweet["text"].dup + if entities = tweet["entities"] and urls = entities["urls"] + urls.each do |url| + text.gsub!(url["url"], "#{url["display_url"]}") + end + end + + text = link_hashtags_in link_handles_in text + + Rinku.auto_link(text, :all, 'target="_blank"').to_s + end + + def user_timeline(screen_name) + JSON.parse(twitter_get(user_timeline_uri_for screen_name)) + end + + def tweet_for(id) + JSON.parse(twitter_get(tweet_uri_for id)) + end + + def raw_tweet_for(id) + twitter_get(tweet_uri_for id) + end + + def twitter_credentials_missing? + consumer_key.blank? || consumer_secret.blank? + end + + protected + + def link_handles_in(text) + text.scan(/\s@(\w+)/).flatten.uniq.each do |handle| + text.gsub!("@#{handle}", [ + "", + "@#{handle}", + "" + ].join) + end + + text + end + + def link_hashtags_in(text) + text.scan(/\s#(\w+)/).flatten.uniq.each do |hashtag| + text.gsub!("##{hashtag}", [ + "", + "##{hashtag}", + "" + ].join) + end + + text + end + + def user_timeline_uri_for(screen_name) + URI.parse "#{BASE_URL}/1.1/statuses/user_timeline.json?screen_name=#{screen_name}&count=50&include_rts=false&exclude_replies=true" + end + + def tweet_uri_for(id) + URI.parse "#{BASE_URL}/1.1/statuses/show.json?id=#{id}" + end + + unless defined? BASE_URL + BASE_URL = 'https://api.twitter.com'.freeze + end + + def twitter_get(uri) + request = Net::HTTP::Get.new(uri) + request.add_field 'Authorization', "Bearer #{bearer_token}" + http(uri).request(request).body + end + + def authorization + request = Net::HTTP::Post.new(auth_uri) + + request.add_field 'Authorization', + "Basic #{bearer_token_credentials}" + request.add_field 'Content-Type', + 'application/x-www-form-urlencoded;charset=UTF-8' + + request.set_form_data 'grant_type' => 'client_credentials' + + http(auth_uri).request(request).body + end + + def bearer_token + @access_token ||= JSON.parse(authorization).fetch('access_token') + end + + def bearer_token_credentials + Base64.strict_encode64( + "#{URI::encode(consumer_key)}:#{URI::encode(consumer_secret)}" + ) + end + + def auth_uri + URI.parse "#{BASE_URL}/oauth2/token" + end + + + def http(uri) + Net::HTTP.new(uri.host, uri.port).tap { |http| http.use_ssl = true } + end + + def consumer_key + SiteSetting.twitter_consumer_key + end + + def consumer_secret + SiteSetting.twitter_consumer_secret + end + + end +end diff --git a/spec/components/oneboxer/twitter_onebox_spec.rb b/spec/components/oneboxer/twitter_onebox_spec.rb index e0e8d1276..b65e2d739 100644 --- a/spec/components/oneboxer/twitter_onebox_spec.rb +++ b/spec/components/oneboxer/twitter_onebox_spec.rb @@ -19,7 +19,7 @@ describe Oneboxer::TwitterOnebox do it 'wraps eack url in a link' do expect(subject.parse(data)['text']).to eq([ "Twitter ", - "", + '', "http://twitter.com", "" ].join)