mirror of
https://github.com/discourse/discourse.git
synced 2024-12-02 03:23:43 +08:00
a64aea38b7
By default, Twitter will return the URL for the avatar image of the tweet poster as the `og:image` value. However, if the `user_generated` attribute is true, we should not use this as the avatar URL as this will be an URL of an image in the tweet itself (e.g., an image belonging to a tweeted news story).
182 lines
4.9 KiB
Ruby
182 lines
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Onebox
|
|
module Engine
|
|
class TwitterStatusOnebox
|
|
include Engine
|
|
include LayoutSupport
|
|
include HTML
|
|
|
|
matches_regexp(/^https?:\/\/(mobile\.|www\.)?twitter\.com\/.+?\/status(es)?\/\d+(\/(video|photo)\/\d?+)?+(\/?\?.*)?\/?$/)
|
|
always_https
|
|
|
|
def http_params
|
|
{ 'User-Agent' => 'DiscourseBot/1.0' }
|
|
end
|
|
|
|
private
|
|
|
|
def get_twitter_data
|
|
response = Onebox::Helpers.fetch_response(url, headers: http_params) rescue nil
|
|
html = Nokogiri::HTML(response)
|
|
twitter_data = {}
|
|
html.css('meta').each do |m|
|
|
if m.attribute('property') && m.attribute('property').to_s.match(/^og:/i)
|
|
m_content = m.attribute('content').to_s.strip
|
|
m_property = m.attribute('property').to_s.gsub('og:', '').gsub(':', '_')
|
|
twitter_data[m_property.to_sym] = m_content
|
|
end
|
|
end
|
|
twitter_data
|
|
end
|
|
|
|
def match
|
|
@match ||= @url.match(%r{twitter\.com/.+?/status(es)?/(?<id>\d+)})
|
|
end
|
|
|
|
def twitter_data
|
|
@twitter_data ||= get_twitter_data
|
|
end
|
|
|
|
def client
|
|
Onebox.options.twitter_client
|
|
end
|
|
|
|
def twitter_api_credentials_present?
|
|
client && !client.twitter_credentials_missing?
|
|
end
|
|
|
|
def raw
|
|
if twitter_api_credentials_present?
|
|
@raw ||= OpenStruct.new(client.status(match[:id]).to_hash)
|
|
else
|
|
super
|
|
end
|
|
end
|
|
|
|
def access(*keys)
|
|
keys.reduce(raw) do |memo, key|
|
|
next unless memo
|
|
memo[key] || memo[key.to_s]
|
|
end
|
|
end
|
|
|
|
def tweet
|
|
if twitter_api_credentials_present?
|
|
client.prettify_tweet(raw)&.strip
|
|
else
|
|
twitter_data[:description].gsub(/“(.+?)”/im) { $1 } if twitter_data[:description]
|
|
end
|
|
end
|
|
|
|
def timestamp
|
|
if twitter_api_credentials_present?
|
|
date = DateTime.strptime(access(:created_at), "%a %b %d %H:%M:%S %z %Y")
|
|
user_offset = access(:user, :utc_offset).to_i
|
|
offset = (user_offset >= 0 ? "+" : "-") + Time.at(user_offset.abs).gmtime.strftime("%H%M")
|
|
date.new_offset(offset).strftime("%-l:%M %p - %-d %b %Y")
|
|
else
|
|
attr_at_css(".tweet-timestamp", 'title')
|
|
end
|
|
end
|
|
|
|
def title
|
|
if twitter_api_credentials_present?
|
|
access(:user, :name)
|
|
else
|
|
attr_at_css('.tweet.permalink-tweet', 'data-name')
|
|
end
|
|
end
|
|
|
|
def screen_name
|
|
if twitter_api_credentials_present?
|
|
access(:user, :screen_name)
|
|
else
|
|
attr_at_css('.tweet.permalink-tweet', 'data-screen-name')
|
|
end
|
|
end
|
|
|
|
def avatar
|
|
if twitter_api_credentials_present?
|
|
access(:user, :profile_image_url_https).sub('normal', '400x400')
|
|
elsif twitter_data[:image]
|
|
twitter_data[:image] unless twitter_data[:image_user_generated]
|
|
end
|
|
end
|
|
|
|
def likes
|
|
if twitter_api_credentials_present?
|
|
prettify_number(access(:favorite_count).to_i)
|
|
else
|
|
attr_at_css(".request-favorited-popup", 'data-compact-localized-count')
|
|
end
|
|
end
|
|
|
|
def retweets
|
|
if twitter_api_credentials_present?
|
|
prettify_number(access(:retweet_count).to_i)
|
|
else
|
|
attr_at_css(".request-retweeted-popup", 'data-compact-localized-count')
|
|
end
|
|
end
|
|
|
|
def quoted_full_name
|
|
if twitter_api_credentials_present?
|
|
access(:quoted_status, :user, :name)
|
|
else
|
|
raw.css('.QuoteTweet-fullname')[0]&.text
|
|
end
|
|
end
|
|
|
|
def quoted_screen_name
|
|
if twitter_api_credentials_present?
|
|
access(:quoted_status, :user, :screen_name)
|
|
else
|
|
attr_at_css(".QuoteTweet-innerContainer", "data-screen-name")
|
|
end
|
|
end
|
|
|
|
def quoted_tweet
|
|
if twitter_api_credentials_present?
|
|
access(:quoted_status, :full_text)
|
|
else
|
|
raw.css('.QuoteTweet-text')[0]&.text
|
|
end
|
|
end
|
|
|
|
def quoted_link
|
|
if twitter_api_credentials_present?
|
|
"https://twitter.com/#{quoted_screen_name}/status/#{access(:quoted_status, :id)}"
|
|
else
|
|
"https://twitter.com#{attr_at_css(".QuoteTweet-innerContainer", "href")}"
|
|
end
|
|
end
|
|
|
|
def prettify_number(count)
|
|
count > 0 ? client.prettify_number(count) : nil
|
|
end
|
|
|
|
def attr_at_css(css_property, attribute_name)
|
|
raw.at_css(css_property)&.attr(attribute_name)
|
|
end
|
|
|
|
def data
|
|
@data ||= {
|
|
link: link,
|
|
tweet: tweet,
|
|
timestamp: timestamp,
|
|
title: title,
|
|
screen_name: screen_name,
|
|
avatar: avatar,
|
|
likes: likes,
|
|
retweets: retweets,
|
|
quoted_tweet: quoted_tweet,
|
|
quoted_full_name: quoted_full_name,
|
|
quoted_screen_name: quoted_screen_name,
|
|
quoted_link: quoted_link
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|