diff --git a/Gemfile b/Gemfile index a5b112e..f38271e 100644 --- a/Gemfile +++ b/Gemfile @@ -37,6 +37,7 @@ gem "reverse_markdown" gem "rexml" gem "babosa" gem "pinboard", git: "https://github.com/dnitza/pinboard", branch: "master" +gem "bskyrb" gem "ogpr" gem "ruby-filemagic", git: "https://github.com/dnitza/ruby-filemagic", branch: "master" gem "webmention" diff --git a/Gemfile.lock b/Gemfile.lock index 1b35141..af5ac58 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -122,6 +122,11 @@ GEM babosa (2.0.0) base64 (0.1.1) bigdecimal (3.1.4) + bskyrb (0.5.3) + date (>= 3.3.3) + httparty (>= 0.21.0) + json (>= 2.0) + xrpc (>= 0.0.4) builder (3.2.4) capistrano (3.18.0) airbrussh (>= 1.0.0) @@ -497,6 +502,10 @@ GEM nokogiri (>= 1.13) xml-simple (1.1.9) rexml + xrpc (0.1.7.2) + date + httparty + json (>= 2.0) zeitwerk (2.6.12) PLATFORMS @@ -506,6 +515,7 @@ PLATFORMS DEPENDENCIES babosa + bskyrb builder capistrano (~> 3.7) capistrano-bundler diff --git a/app/actions/site/config.rb b/app/actions/site/config.rb index 1067df0..270b98b 100644 --- a/app/actions/site/config.rb +++ b/app/actions/site/config.rb @@ -29,6 +29,10 @@ module Adamantium { uid: "https://pinboard.in", name: "Pinboard" + }, + { + uid: "https://bsky.app", + name: "Blue Sky" } ] }.to_json @@ -44,6 +48,10 @@ module Adamantium { uid: "https://pinboard.in", name: "Pinboard" + }, + { + uid: "https://bsky.app", + name: "Blue Sky" } ] }.to_json diff --git a/app/commands/posts/syndicate.rb b/app/commands/posts/syndicate.rb index 9506f4c..cef0143 100644 --- a/app/commands/posts/syndicate.rb +++ b/app/commands/posts/syndicate.rb @@ -10,6 +10,7 @@ module Adamantium include Deps["settings", "syndication.mastodon", + "syndication.blue_sky", add_post_syndication_source: "commands.posts.add_syndication_source", send_to_dayone: "syndication.dayone", ] @@ -23,6 +24,12 @@ module Adamantium add_post_syndication_source.call(post_id, :mastodon, res.value!) if res.success? end + if syndicate_to.include? :blue_sky + res = blue_sky.call(post: post) + + add_post_syndication_source.call(post_id, :blue_sky, res.value!) if res.success? + end + if post[:category].include? "weekly" send_to_dayone.call(name: post[:name], content: post[:content]) end @@ -35,6 +42,7 @@ module Adamantium def syndication_targets(syndicate_to) targets = [] targets << :mastodon if syndicate_to.any? { |url| settings.mastodon_server.match(/#{url}/) } + targets << :blue_sky if syndicate_to.any? { |url| settings.blue_sky_url.match(/#{url}/) } targets end end diff --git a/app/templates/shared/_blue_sky.html.slim b/app/templates/shared/_blue_sky.html.slim new file mode 100644 index 0000000..16a98c1 --- /dev/null +++ b/app/templates/shared/_blue_sky.html.slim @@ -0,0 +1,2 @@ +- w_class = defined?(width) ? width : "w-6" + diff --git a/config/providers/clients.rb b/config/providers/clients.rb index 334cbab..b293054 100644 --- a/config/providers/clients.rb +++ b/config/providers/clients.rb @@ -1,6 +1,7 @@ Hanami.app.register_provider :clients, namespace: true do start do register "mastodon", Adamantium::Client::Mastodon.new + register "blue_sky", Adamantium::Client::BlueSky.new register "omdb", Adamantium::Client::Omdb.new(api_key: target["settings"].omdb_api_key) end end diff --git a/config/providers/syndication.rb b/config/providers/syndication.rb index 550c9a4..2d71e48 100644 --- a/config/providers/syndication.rb +++ b/config/providers/syndication.rb @@ -1,6 +1,7 @@ Hanami.app.register_provider :syndication, namespace: true do start do register "mastodon", Adamantium::Syndication::Mastodon.new + register "blue_sky", Adamantium::Syndication::BlueSky.new register "raindrop", Adamantium::Syndication::Raindrop.new(api_key: "Bearer #{target["settings"].raindrop_api_key}") register "dayone", Adamantium::Syndication::Dayone.new( username: target["settings"].smtp_username, diff --git a/config/settings.rb b/config/settings.rb index 5a4c8ae..2c36083 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -22,6 +22,10 @@ module Adamantium setting :mastodon_url, default: nil setting :fed_bridge_url, default: nil + setting :blue_sky_url, default: nil + setting :blue_sky_username, default: nil + setting :blue_sky_password, default: nil + setting :webmention_url, default: nil setting :pingback_url, default: nil setting :webmention_token, default: nil diff --git a/lib/adamantium/client/blue_sky.rb b/lib/adamantium/client/blue_sky.rb new file mode 100644 index 0000000..b97bc8e --- /dev/null +++ b/lib/adamantium/client/blue_sky.rb @@ -0,0 +1,52 @@ +require "bskyrb" +require "digest" +require "tempfile" +require "open-uri" +require "dry/monads" +require "sanitize" + +module Adamantium + module Client + class BlueSky + include Dry::Monads[:result] + include Deps["settings"] + + def create_post(post:) + unless settings.mastodon_token && settings.mastodon_server + return Failure(:no_mastodon_credentials) + end + + content = if post[:name] + "#{post[:name]} — #{settings.micropub_site_url}/post/#{post[:slug]}" + else + "#{sanitze_post(post[:content])} \r\n\r\n 🔗 #{settings.micropub_site_url}/post/#{post[:slug]}" + end + + credentials = Bskyrb::Credentials.new(settings.blue_sky_username, settings.blue_sky_password) + session = Bskyrb::Session.new(credentials, settings.blue_sky_url) + client = Bskyrb::RecordManager.new(session) + + response = client.create_post content + + if response["uri"] && response["uri"] != "" + Success(response["uri"]) + else + Failure(response.message) + end + end + + private + + def sanitze_post(content) + replace_links = lambda { |env| + return unless env[:node_name] == "a" + node = env[:node] + url = node[:href] + env[:node].replace(url) + } + + Sanitize.fragment(content, transformers: [replace_links]).strip + end + end + end +end diff --git a/lib/adamantium/syndication/blue_sky.rb b/lib/adamantium/syndication/blue_sky.rb new file mode 100644 index 0000000..72c0b9c --- /dev/null +++ b/lib/adamantium/syndication/blue_sky.rb @@ -0,0 +1,20 @@ +require "dry/monads" + +module Adamantium + module Syndication + class BlueSky + include Dry::Monads[:result] + include Deps[blue_sky_client: "clients.blue_sky"] + + def call(post:) + response = blue_sky.create_post(post: post) + + if response.success? + Success(response.value!) + else + Failure(:failed_to_syndicate) + end + end + end + end +end diff --git a/spec/adamantium/unit/commands/posts/syndicate_spec.rb b/spec/adamantium/unit/commands/posts/syndicate_spec.rb index 039d9fb..ae198b6 100644 --- a/spec/adamantium/unit/commands/posts/syndicate_spec.rb +++ b/spec/adamantium/unit/commands/posts/syndicate_spec.rb @@ -4,7 +4,7 @@ require "dry/monads" RSpec.describe Adamantium::Commands::Posts::Syndicate do include Dry::Monads[:result] - let(:settings) { double("settings", mastodon_server: "https://mastodon.example/@tester") } + let(:settings) { double("settings", mastodon_server: "https://mastodon.example/@tester", blue_sky_url: "https://bluesky.app") } let(:mastodon_client) { double("Adamantium::Client::Mastodon") } let(:mastodon_syndicator) { Adamantium::Syndication::Mastodon.new(mastodon_client: mastodon_client) } let(:post) { {url: "example.com", syndicate_to: ["https://mastodon.example", "https://pinboard.in"], category: []} }