diff --git a/.gitignore b/.gitignore index f1f9367..90f7552 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ Capfile config/deploy.rb config/deploy/production.rb config/systemd/que.service.erb +config/mail_room.yaml script/deploy tmp/* public/media diff --git a/Gemfile b/Gemfile index fb83a5e..d33b549 100644 --- a/Gemfile +++ b/Gemfile @@ -44,7 +44,8 @@ gem "babosa" gem "bskyrb" gem "ogpr" gem "ruby-filemagic", git: "https://github.com/dnitza/ruby-filemagic", branch: "master" -gem "webmention" +gem "mail_room", github: "dNitza/mail_room", branch: "master" +gem "charlock_holmes" gem "sanitize" gem "time_math2", require: "time_math" gem "jwt" diff --git a/Gemfile.lock b/Gemfile.lock index 4413966..c1c23ed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,13 @@ +GIT + remote: https://github.com/dNitza/mail_room.git + revision: e36e9b457e61227f7b631246e63d35fb17d921fa + branch: master + specs: + mail_room (0.11.1) + jwt (>= 2.0) + net-imap (>= 0.2.1) + oauth2 (>= 1.4.4, < 3) + GIT remote: https://github.com/dNitza/scraperd.git revision: a8fc96f56e64a13e70490ded1dd73345dc7bc9f2 @@ -63,6 +73,7 @@ GEM sshkit (~> 1.3) capistrano-systemd-multiservice (0.1.0.beta13) capistrano (~> 3.7) + charlock_holmes (0.7.7) chronic (0.10.2) coderay (1.1.3) concurrent-ruby (1.2.3) @@ -147,10 +158,11 @@ GEM ed25519 (1.3.0) faker (3.2.3) i18n (>= 1.8.11, < 2) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-net_http (3.1.0) + net-http ffi (1.16.3) - ffi-compiler (1.0.1) - ffi (>= 1.0.0) - rake formatador (1.1.0) georuby (2.5.2) gnuplot (2.6.2) @@ -226,16 +238,9 @@ GEM zeitwerk (~> 2.6) hansi (0.2.1) hashie (2.1.2) - http (5.2.0) - addressable (~> 2.8) - base64 (~> 0.1) - http-cookie (~> 1.0) - http-form_data (~> 2.2) - llhttp-ffi (~> 0.5.0) http-accept (1.7.0) http-cookie (1.0.5) domain_name (~> 0.5) - http-form_data (2.3.0) httparty (0.21.0) mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) @@ -245,10 +250,6 @@ GEM image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) - indieweb-endpoints (8.0.0) - http (~> 5.0) - link-header-parser (~> 5.0) - nokogiri (>= 1.13) io-console (0.7.2) irb (1.11.2) rdoc @@ -260,14 +261,10 @@ GEM lastfm (1.27.4) httparty xml-simple - link-header-parser (5.1.1) lint_roller (1.1.0) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - llhttp-ffi (0.5.0) - ffi-compiler (~> 1.0) - rake (~> 13.0) lumberjack (1.2.10) mail (2.8.1) mini_mime (>= 0.1.1) @@ -290,6 +287,8 @@ GEM mustermann (= 3.0.0) mutex_m (0.2.0) nenv (0.3.0) + net-http (0.4.1) + uri net-imap (0.4.10) date net-protocol @@ -315,6 +314,13 @@ GEM notiffany (0.1.3) nenv (~> 0.1) shellany (~> 0.0) + oauth2 (2.0.9) + faraday (>= 0.17.3, < 3.0) + jwt (>= 1.0, < 3.0) + multi_xml (~> 0.5) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0) + version_gem (~> 1.1) ogpr (1.1.0) nokogiri (~> 1.8) rest-client (~> 2.1.0) @@ -449,6 +455,9 @@ GEM slim (5.2.1) temple (~> 0.10.0) tilt (>= 2.1.0) + snaky_hash (2.0.1) + hashie + version_gem (~> 1.1, >= 1.1.1) sshkit (1.22.0) mutex_m net-scp (>= 1.1.2) @@ -479,11 +488,9 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) + uri (0.13.0) + version_gem (1.1.3) warning (1.3.0) - webmention (7.0.0) - http (~> 5.0) - indieweb-endpoints (~> 8.0) - nokogiri (>= 1.13) whenever (1.0.0) chronic (>= 0.6.3) xml-simple (1.1.9) @@ -510,6 +517,7 @@ DEPENDENCIES capistrano-rbenv (~> 2.2) capistrano-systemd-multiservice capistrano3-puma! + charlock_holmes connection_pool csv database_cleaner-sequel @@ -537,6 +545,7 @@ DEPENDENCIES jwt lastfm (~> 1.27) mail + mail_room! matrix mini_magick ogpr @@ -565,7 +574,6 @@ DEPENDENCIES time_math2 timecop warning - webmention whenever RUBY VERSION diff --git a/config/systemd/mail_room.service.erb b/config/systemd/mail_room.service.erb new file mode 100644 index 0000000..b80c56c --- /dev/null +++ b/config/systemd/mail_room.service.erb @@ -0,0 +1,13 @@ +[Unit] +Description = blog mail_room worker + +[Service] +Type=simple +Environment = PWD=/home/blog/current +WorkingDirectory = /home/blog/current +ExecStart = /home/deployer/.rbenv/shims/bundle exec mail_room -c config/mail_room.yaml +Restart=always +User=deployer + +[Install] +WantedBy = multi-user.target \ No newline at end of file diff --git a/db/migrate/20240309210727_add_commentable_to_posts.rb b/db/migrate/20240309210727_add_commentable_to_posts.rb new file mode 100644 index 0000000..cfbc0bd --- /dev/null +++ b/db/migrate/20240309210727_add_commentable_to_posts.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +ROM::SQL.migration do + change do + alter_table :posts do + add_column :commentable, :boolean, default: false + end + end +end diff --git a/lib/adamantium/jobs/archive_deleted_webmentions.rb b/lib/adamantium/jobs/archive_deleted_webmentions.rb index 2e6629d..f05cc7e 100644 --- a/lib/adamantium/jobs/archive_deleted_webmentions.rb +++ b/lib/adamantium/jobs/archive_deleted_webmentions.rb @@ -7,7 +7,7 @@ module Adamantium def run webmention_repo = Admin::Container["repos.webmention_repo"] - webmentions = webmention_repo.list_all + webmentions = webmention_repo.list_all_for_check webmentions.each do |webmention| code = HTTParty.get(webmention.source_url, follow_redirects: false).code diff --git a/lib/adamantium/jobs/save_email_reply.rb b/lib/adamantium/jobs/save_email_reply.rb new file mode 100644 index 0000000..b3e741e --- /dev/null +++ b/lib/adamantium/jobs/save_email_reply.rb @@ -0,0 +1,38 @@ +require "que" + +module Adamantium + module Jobs + class SaveEmailReply < Que::Job + def run(message) + webmentions_repo = Micropub::Container["repos.webmentions_repo"] + post_repo = Micropub::Container["repos.post_repo"] + settings = Micropub::Container["settings"] + renderer = Micropub::Container["renderers.markdown"] + + mail = Mail::Message.new(message) + + return unless mail.subject == "About that post of yours" + + reply_content, in_reply_to = mail.body.decoded.split("---") + post_url = URI.join(settings.micropub_site_url, in_reply_to.match(URI::DEFAULT_PARSER.make_regexp)[7]&.gsub(")", "")).to_s + + slug = post_url.split("/").last + post = post_repo.fetch!(slug) + + return unless post.commentable + + reply = {} + reply[:type] = "reply" + reply[:author_name] = mail.from.first + reply[:published_at] = nil + reply[:content_html] = renderer.call(content: reply_content) + reply[:content_text] = reply_content + reply[:source_url] = "email" + reply[:target_url] = post_url + reply[:post_id] = post.id + + webmentions_repo.create(reply) + end + end + end +end diff --git a/slices/admin/commands/posts/update.rb b/slices/admin/commands/posts/update.rb index 28d2d09..8d5c218 100644 --- a/slices/admin/commands/posts/update.rb +++ b/slices/admin/commands/posts/update.rb @@ -10,6 +10,7 @@ module Admin def call(params:) attrs_to_replace = {} attrs_to_replace[:content] = markdown.call(content: params[:body]) if params[:body] + attrs_to_replace[:commentable] = params[:commentable] tags = params[:tags].split(",").map(&:strip) diff --git a/slices/admin/repos/webmention_repo.rb b/slices/admin/repos/webmention_repo.rb index af95fb9..5a1f768 100644 --- a/slices/admin/repos/webmention_repo.rb +++ b/slices/admin/repos/webmention_repo.rb @@ -3,6 +3,13 @@ module Admin class WebmentionRepo < Adamantium::Repo[:webmentions] commands update: :by_pk + def list_all_for_check + webmentions + .exclude(source_url: "email") + .order(:id) + .to_a + end + def list_all webmentions .order(:id) diff --git a/slices/admin/templates/posts/show.html.slim b/slices/admin/templates/posts/show.html.slim index 2596a2e..bd1f16a 100644 --- a/slices/admin/templates/posts/show.html.slim +++ b/slices/admin/templates/posts/show.html.slim @@ -30,6 +30,9 @@ article class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 d form action="/admin/post/#{post.id}/update" method="POST" textarea name="body" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2 mb-4" x-data="{ resize: () => { $el.style.height = '5px'; $el.style.height = $el.scrollHeight + 'px' } }" x-init="resize()" @input="resize()" == markdown_body + fieldset class="mb-4 flex" + label for="commentable" class="mr-2" Commentable? + input class="mt-2" type="checkbox" value="true" id="commentable" name="commentable" switch="switch" checked=post.commentable fieldset class="mb-4 flex" label for="tags" class="mr-2" Tags: input type="text" name="tags" id="tags" class="w-full px-1 border rounded" value="#{post.tags.map(&:label).join(", ")}" diff --git a/slices/admin/templates/webmentions/index.html.slim b/slices/admin/templates/webmentions/index.html.slim index 9783d10..be8d3b7 100644 --- a/slices/admin/templates/webmentions/index.html.slim +++ b/slices/admin/templates/webmentions/index.html.slim @@ -32,7 +32,10 @@ div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark: span == webmention.content_html td - a href="#{webmention.source_url}" Source + - if webmention.source_url == "email" + = "email" + - else + a href="#{webmention.source_url}" Source td a href="#{webmention.target_url}" Target td diff --git a/slices/main/templates/posts/show.html.slim b/slices/main/templates/posts/show.html.slim index 9220b3b..5c63c83 100644 --- a/slices/main/templates/posts/show.html.slim +++ b/slices/main/templates/posts/show.html.slim @@ -50,19 +50,32 @@ article class="h-entry" - if post.location img loading="lazy" class="shadow-solid shadow-pink-100 dark:shadow-pink-200 rounded mb-4" src=post.large_map - -if post.webmentions && post.webmentions.count > 0 + - if post.webmentions && post.webmentions.count == 0 && post.commentable + a href="mailto:blog@dnitza.com?subject=About that post of yours&body=%0A%0A---%0A(In reply to #{post.permalink})" Reply + - if post.webmentions && post.webmentions.count > 0 div class="mt-12" - h3 #{post.webmentions.count} Comment#{post.webmentions.count != 1 ? "s" : ""} + h3 + = "#{post.webmentions.count} Comment#{post.webmentions.count != 1 ? "s" : ""}" + - if post.commentable + == " · " + a href="mailto:blog@dnitza.com?subject=About that post of yours&body=%0A%0A---%0A(In reply to #{post.permalink})" reply - post.webmentions.each do |mention| div class="prose-p:m-1 mb-6 p-8 bg-orange-100 dark:bg-indigo-900 squircle" div class="flex h-8" - img loading="lazy" class="w-8 rounded-full m-0 mr-2" src=mention.author_photo - a class="block text-orange-700 dark:text-violet-300 no-underline hover:underline" href=mention.author_url - = mention.author_name + - if mention.author_photo != "" + img loading="lazy" class="w-8 rounded-full m-0 mr-2" src=mention.author_photo + - if mention.author_url == "" + = "From #{mention.author_name}" + - else + a class="block text-orange-700 dark:text-violet-300 no-underline hover:underline" href=mention.author_url + = mention.author_name div class="prose dark:prose-invert dark:text-indigo-250 prose-a:text-orange-700 dark:prose-a:text-violet-300 prose-a:no-underline hover:prose-a:underline" == mention.content_html div class="text-sm" - a class="no-underline hover:underline text-orange-900 dark:text-violet-400" href=mention.source_url + - if mention.source_url != "email" + a class="no-underline hover:underline text-orange-900 dark:text-violet-400" href=mention.source_url + = mention.published_at.strftime("%e %B, %Y") + - else = mention.published_at.strftime("%e %B, %Y") div class="mb-12" - if trip