diff --git a/app/logical/mastodon_api_client.rb b/app/logical/mastodon_api_client.rb new file mode 100644 index 000000000..0b837d669 --- /dev/null +++ b/app/logical/mastodon_api_client.rb @@ -0,0 +1,84 @@ +class MastodonApiClient + extend Memoist + attr_reader :json + + def initialize(site_name, id) + @site_name = site_name + + return if access_token.blank? + + begin + @json = JSON.parse(access_token.get("/api/v1/statuses/#{id}").body) + rescue StandardError + @json = {} + end + end + + def profile_url + json.dig("account", "url") + end + + def account_name + json.dig("account", "username") + end + + def display_name + json.dig("account", "display_name") + end + + def account_id + json.dig("account", "id") + end + + def image_url + image_urls.first + end + + def image_urls + json["media_attachments"].to_a.map {|x| x["url"]} + end + + def tags + json["tags"].to_a.map { |tag| [tag["name"], tag["url"]] } + end + + def commentary + commentary = "" + commentary << "

#{json["spoiler_text"]}

" if json["spoiler_text"].present? + commentary << json["content"] + commentary + end + + def to_h + json + end + + def fetch_access_token + Cache.get("#{@site_name}-token") do + result = client.client_credentials.get_token + result.token + end + end + + def access_token + return if client.blank? + OAuth2::AccessToken.new(client, fetch_access_token) + end + + def client + case @site_name + when "pawoo.net" + client_id = Danbooru.config.pawoo_client_id + client_secret = Danbooru.config.pawoo_client_secret + when "baraag.net" + client_id = Danbooru.config.baraag_client_id + client_secret = Danbooru.config.baraag_client_secret + end + + return unless client_id && client_secret + + OAuth2::Client.new(client_id, client_secret, :site => "https://#{@site_name}") + end + + memoize :client +end diff --git a/app/logical/pawoo_api_client.rb b/app/logical/pawoo_api_client.rb deleted file mode 100644 index f74d42e38..000000000 --- a/app/logical/pawoo_api_client.rb +++ /dev/null @@ -1,156 +0,0 @@ -class PawooApiClient - extend Memoist - - PROFILE1 = %r!\Ahttps?://pawoo\.net/web/accounts/(\d+)! - PROFILE2 = %r!\Ahttps?://pawoo\.net/@([^/]+)! - STATUS1 = %r!\Ahttps?://pawoo\.net/web/statuses/(\d+)! - STATUS2 = %r!\Ahttps?://pawoo\.net/@.+?/([^/]+)! - - class MissingConfigurationError < StandardError; end - - class Account - attr_reader :json - - def self.is_match?(url) - if url =~ PROFILE1 - return $1 - end - - if url =~ PROFILE2 - return $1 - end - - false - end - - def initialize(json) - @json = json - end - - def profile_url - json["url"] - end - - def account_name - json["username"] - end - - def image_url - nil - end - - def image_urls - [] - end - - def tags - [] - end - - def commentary - nil - end - - def to_h - json - end - end - - class Status - attr_reader :json - - def self.is_match?(url) - if url =~ STATUS1 - return $1 - end - - if url =~ STATUS2 - return $1 - end - - false - end - - def initialize(json) - @json = json - end - - def profile_url - json["account"]["url"] - end - - def account_name - json["account"]["username"] - end - - def image_url - image_urls.first - end - - def image_urls - json["media_attachments"].map {|x| x["url"]} - end - - def tags - json["tags"].map { |tag| [tag["name"], tag["url"]] } - end - - def commentary - commentary = "" - commentary << "

#{json["spoiler_text"]}

" if json["spoiler_text"].present? - commentary << json["content"] - commentary - end - - def to_h - json - end - end - - def get(url) - if id = Status.is_match?(url) - begin - data = JSON.parse(access_token.get("/api/v1/statuses/#{id}").body) - rescue - data = { - "account" => {}, - "media_attachments" => [], - "tags" => [], - "content" => "", - } - end - return Status.new(data) - end - - if id = Account.is_match?(url) - begin - data = JSON.parse(access_token.get("/api/v1/accounts/#{id}").body) - rescue - data = {} - end - Account.new(data) - end - end - - private - - def fetch_access_token - raise MissingConfigurationError, "missing pawoo client id" if Danbooru.config.pawoo_client_id.nil? - raise MissingConfigurationError, "missing pawoo client secret" if Danbooru.config.pawoo_client_secret.nil? - - Cache.get("pawoo-token") do - result = client.client_credentials.get_token - result.token - end - end - - def access_token - OAuth2::AccessToken.new(client, fetch_access_token) - end - - def client - OAuth2::Client.new(Danbooru.config.pawoo_client_id, Danbooru.config.pawoo_client_secret, :site => "https://pawoo.net") - end - - memoize :client -end diff --git a/app/logical/sources/strategies.rb b/app/logical/sources/strategies.rb index fddc1fdd3..895a9a6bd 100644 --- a/app/logical/sources/strategies.rb +++ b/app/logical/sources/strategies.rb @@ -11,7 +11,7 @@ module Sources Strategies::Tumblr, Strategies::ArtStation, Strategies::Nijie, - Strategies::Pawoo, + Strategies::Mastodon, Strategies::Moebooru, Strategies::HentaiFoundry, Strategies::Weibo, diff --git a/app/logical/sources/strategies/pawoo.rb b/app/logical/sources/strategies/mastodon.rb similarity index 60% rename from app/logical/sources/strategies/pawoo.rb rename to app/logical/sources/strategies/mastodon.rb index f170eb100..3fef4810e 100644 --- a/app/logical/sources/strategies/pawoo.rb +++ b/app/logical/sources/strategies/mastodon.rb @@ -15,25 +15,28 @@ # * https://pawoo.net/oauth_authentications/17230064 module Sources::Strategies - class Pawoo < Base - HOST = %r{\Ahttps?://(www\.)?pawoo\.net}i - IMAGE = %r{\Ahttps?://img\.pawoo\.net/media_attachments/files/(\d+/\d+/\d+)} + class Mastodon < Base + HOST = %r{\Ahttps?://(?:www\.)?(?pawoo\.net|baraag\.net)}i + IMAGE = %r{\Ahttps?://(?:img\.pawoo\.net|baraag\.net)/media_attachments/files/(\d+/\d+/\d+)} NAMED_PROFILE = %r{#{HOST}/@(?\w+)}i - ID_PROFILE = %r{#{HOST}/web/accounts/(?\d+)} + ID_PROFILE = %r{#{HOST}/web/accounts/(?\d+)} STATUS1 = %r{\A#{HOST}/web/statuses/(?\d+)} STATUS2 = %r{\A#{NAMED_PROFILE}/(?\d+)} - def self.enabled? - Danbooru.config.pawoo_client_id.present? && Danbooru.config.pawoo_client_secret.present? - end - def domains - ["pawoo.net"] + ["pawoo.net", "baraag.net"] end def site_name - "Pawoo" + parsed_url.domain + end + + def file_host + case site_name + when "pawoo.net" then "img.pawoo.net" + else site_name + end end def image_url @@ -41,49 +44,56 @@ module Sources::Strategies end def image_urls - if url =~ %r{#{IMAGE}/small/([a-z0-9]+\.\w+)\z}i - ["https://img.pawoo.net/media_attachments/files/#{$1}/original/#{$2}"] - elsif url =~ %r{#{IMAGE}/original/([a-z0-9]+\.\w+)\z}i - [url] + if url =~ %r{#{IMAGE}/(?:small|original)/([a-z0-9]+\.\w+)\z}i + ["https://#{file_host}/media_attachments/files/#{$1}/original/#{$2}"] else api_response.image_urls end end def page_url - [url, referer_url].each do |x| - if PawooApiClient::Status.is_match?(x) - return x - end - end + artist_name = artist_name_from_url + status_id = status_id_from_url + return if status_id.blank? - super + if artist_name.present? + "https://#{site_name}/@#{artist_name}/#{status_id}" + else + "https://#{site_name}/web/statuses/#{status_id}" + end end def profile_url - if url =~ PawooApiClient::PROFILE2 - "https://pawoo.net/@#{$1}" - elsif api_response.profile_url.blank? - url - else + if artist_name_from_url.present? + "https://#{site_name}/@#{artist_name_from_url}" + elsif api_response.present? && api_response.profile_url.present? api_response.profile_url end end + def account_url + return if account_id.blank? + "https://#{site_name}/web/accounts/#{account_id}" + end + + def profile_urls + [profile_url, account_url].compact + end + def artist_name api_response.account_name end def artist_name_from_url - if url =~ NAMED_PROFILE - url[NAMED_PROFILE, :artist_name] - end + url[NAMED_PROFILE, :artist_name] end - def artist_id_from_url - if url =~ ID_PROFILE - url[ID_PROFILE, :artist_name] - end + def other_names + [api_response.display_name] + end + + def account_id + url[ID_PROFILE, :account_id] || api_response.account_id end def status_id_from_url @@ -99,15 +109,7 @@ module Sources::Strategies end def normalize_for_source - artist_name = artist_name_from_url - status_id = status_id_from_url - return if status_id.blank? - - if artist_name.present? - "https://pawoo.net/@#{artist_name}/#{status_id}" - else - "https://pawoo.net/web/statuses/#{status_id}" - end + page_url end def dtext_artist_commentary_desc @@ -121,13 +123,8 @@ module Sources::Strategies end def api_response - [url, referer_url].each do |x| - if (client = PawooApiClient.new.get(x)) - return client - end - end - - nil + return {} if status_id_from_url.blank? + MastodonApiClient.new(site_name, status_id_from_url) end memoize :api_response end diff --git a/config/danbooru_default_config.rb b/config/danbooru_default_config.rb index 98a4d1710..291d79974 100644 --- a/config/danbooru_default_config.rb +++ b/config/danbooru_default_config.rb @@ -330,6 +330,14 @@ module Danbooru nil end + def baraag_client_id + nil + end + + def baraag_client_secret + nil + end + # 1. Register app at https://www.tumblr.com/oauth/register. # 2. Copy "OAuth Consumer Key" from https://www.tumblr.com/oauth/apps. def tumblr_consumer_key diff --git a/test/unit/sources/pawoo_test.rb b/test/unit/sources/mastodon_test.rb similarity index 77% rename from test/unit/sources/pawoo_test.rb rename to test/unit/sources/mastodon_test.rb index a20485085..59d31d0f7 100644 --- a/test/unit/sources/pawoo_test.rb +++ b/test/unit/sources/mastodon_test.rb @@ -1,7 +1,7 @@ require 'test_helper' module Sources - class PawooTest < ActiveSupport::TestCase + class MastodonTest < ActiveSupport::TestCase context "The source site for a https://pawoo.net/web/status/$id url" do setup do skip "Pawoo keys not set" unless Danbooru.config.pawoo_client_id @@ -38,7 +38,9 @@ module Sources end should "get the profile" do - assert_equal("https://pawoo.net/@evazion", @site.profile_url) + profiles = %w[https://pawoo.net/@evazion https://pawoo.net/web/accounts/47806] + assert_equal(profiles.first, @site.profile_url) + assert_equal(profiles, @site.profile_urls) end should "get the artist name" do @@ -68,7 +70,7 @@ module Sources end should "get the dtext-ified commentary" do - desc = <<-EOS.strip_heredoc.chomp + desc = <<-DESC.strip_heredoc.chomp test post please ignore blah blah blah @@ -76,7 +78,7 @@ module Sources this is a test 🍕 "#foo":[https://pawoo.net/tags/foo] "#bar":[https://pawoo.net/tags/bar] "#baz":[https://pawoo.net/tags/baz] - EOS + DESC assert_equal(desc, @site.dtext_artist_commentary_desc) end @@ -95,21 +97,40 @@ module Sources end end + context "A baraag url" do + setup do + skip "Baraag keys not set" unless Danbooru.config.baraag_client_id + @url = "https://baraag.net/@bardbot/105732813175612920" + @site = Sources::Strategies.find(@url) + end + + should "work" do + assert_equal("https://baraag.net/@bardbot", @site.profile_url) + assert_equal(["https://baraag.net/system/media_attachments/files/105/732/803/241/495/700/original/556e1eb7f5ca610f.png"], @site.image_urls) + assert_equal("bardbot", @site.artist_name) + assert_equal("🍌", @site.dtext_artist_commentary_desc) + end + end + context "normalizing for source" do should "normalize correctly" do source1 = "https://pawoo.net/@evazion/19451018/" source2 = "https://pawoo.net/web/statuses/19451018/favorites" + source3 = "https://baraag.net/@bardbot/105732813175612920/" assert_equal("https://pawoo.net/@evazion/19451018", Sources::Strategies.normalize_source(source1)) assert_equal("https://pawoo.net/web/statuses/19451018", Sources::Strategies.normalize_source(source2)) + assert_equal("https://baraag.net/@bardbot/105732813175612920", Sources::Strategies.normalize_source(source3)) end should "avoid normalizing unnormalizable urls" do bad_source1 = "https://img.pawoo.net/media_attachments/files/001/297/997/original/c4272a09570757c2.png" bad_source2 = "https://pawoo.net/@evazion/media" + bad_source3 = "https://baraag.net/system/media_attachments/files/105/732/803/241/495/700/original/556e1eb7f5ca610f.png" assert_equal(bad_source1, Sources::Strategies.normalize_source(bad_source1)) assert_equal(bad_source2, Sources::Strategies.normalize_source(bad_source2)) + assert_equal(bad_source3, Sources::Strategies.normalize_source(bad_source3)) end end end