diff options
author | pdp8 <pdp8@pdp8.info> | 2023-07-02 00:37:33 +0200 |
---|---|---|
committer | pdp8 <pdp8@pdp8.info> | 2023-07-02 00:37:33 +0200 |
commit | 7f38d569d8dd2491d1b9b8bc0ff1ae016b02f34f (patch) | |
tree | 5ddd0eba0dce147e8b799351a9f3e20945a08118 /helpers.rb | |
parent | 086709cae3da7a01a011fe906004c8685fdd2ed0 (diff) |
activity sending/storage unified (send_signed -> outbox)
Diffstat (limited to 'helpers.rb')
-rw-r--r-- | helpers.rb | 89 |
1 files changed, 61 insertions, 28 deletions
@@ -1,6 +1,7 @@ # frozen_string_literal: true require 'English' + helpers do def curl(ext, url) p url @@ -8,7 +9,6 @@ helpers do $CHILD_STATUS.success? ? response : nil end - # def fetch(url, accept = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"') def fetch(url, accept = 'application/activity+json') response = curl("-H 'Accept: #{accept}'", url) response ? JSON.parse(response) : nil @@ -16,36 +16,60 @@ helpers do # https://github.com/mastodon/mastodon/blob/main/app/lib/request.rb # , url - def send_signed(object) + def outbox(type, object, recipients, add_recipients = false) + activity = { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'type' => type, + 'actor' => ACTOR, + 'object' => object + } + + now = Time.now + date = now.strftime('%Y-%m-%dT%H:%M:%S.%N') + httpdate = now.utc.httpdate + basename = "#{date}.json" + activity['id'] = File.join(OUTBOX_URL, basename) + activity['published'] = httpdate + if activity['object'] and activity['object']['type'] and !activity['object']['id'] + rel_path = File.join activity['object']['type'].downcase, basename + activity['object']['published'] = httpdate + activity['object']['id'] = File.join(OUTBOX_URL, rel_path) + File.open(File.join(OUTBOX_DIR, rel_path), 'w+') { |f| f.puts activity.to_json } + end + File.open(File.join(OUTBOX_DIR, basename), 'w+') { |f| f.puts activity.to_json } + keypair = OpenSSL::PKey::RSA.new(File.read('private.pem')) - date = Time.now.utc.httpdate - body = object.to_json + body = activity.to_json sha256 = OpenSSL::Digest.new('SHA256') digest = "SHA-256=#{sha256.base64digest(body)}" - recipients = [object['to'], object['cc'], object['bto'], object['bcc'], object['audience']].flatten.compact.uniq - recipients.each do |url| - next if url == 'https://www.w3.org/ns/activitystreams#Public' - - host = URI.parse(url).host - inbox = fetch(url)['inbox'] - if inbox - request_uri = URI(inbox).request_uri - - signed_string = "(request-target): post #{request_uri}\nhost: #{host}\ndate: #{date}\ndigest: #{digest}\ncontent-type: application/activity+json" - signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string)) - signed_header = "keyId=\"#{ACTOR}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"#{signature}\"" - - curl( - "-X POST -H 'Content-Type: application/activity+json' -H 'Host: #{host}' -H 'Date: #{date}' -H 'Digest: #{digest}' -H 'Signature: #{signed_header}' -d '#{body}'", inbox - ) - else - p "No inbox for #{url}" - end + + # assumes that recipient collections have been expanded by sender + # put all recipients into 'to', avoid 'cc' 'bto' 'bcc' 'audience' !! + activity['to'] = recipients if add_recipients + inboxes = if recipients.include? 'https://www.w3.org/ns/activitystreams#Public' + people.collect { |p| p[2] }.uniq # cached sharedInboxes + else + [] + end + recipients.uniq.each do |url| + next if [ACTOR, 'https://www.w3.org/ns/activitystreams#Public'].include? url + + actor = fetch url + next unless actor and actor['inbox'] + + inbox = actor['endpoints']['sharedInbox'] + inboxes << (inbox || actor['inbox']) end - end + inboxes.compact.uniq.each do |inbox| + uri = URI(inbox) + string = "(request-target): post #{uri.request_uri}\nhost: #{uri.host}\ndate: #{httpdate}\ndigest: #{digest}\ncontent-type: application/activity+json" + signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), string)) + signed_header = "keyId=\"#{ACTOR}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"#{signature}\"" - def people - File.read('cache/people.tsv').split("\n").collect { |l| l.chomp.split("\t") } + curl( + "-X POST -H 'Content-Type: application/activity+json' -H 'Host: #{uri.host}' -H 'Date: #{httpdate}' -H 'Digest: #{digest}' -H 'Signature: #{signed_header}' -d '#{body}'", inbox + ) + end end def mention(actor) @@ -55,7 +79,7 @@ helpers do return nil unless a mention = "#{a['preferredUsername']}@#{URI(actor).host}" - File.open('cache/people.tsv', 'a') { |f| f.puts "#{mention}\t#{actor}" } + cache mention, actor, a mention else person[0][0] @@ -73,10 +97,19 @@ helpers do actor = a['links'].select do |l| l['rel'] == 'self' end[0]['href'] - File.open('cache/people.tsv', 'a') { |f| f.puts "#{mention}\t#{actor}" } + cache mention, actor, a actor else actors[0][1] end end + + def people + File.read('cache/people.tsv').split("\n").collect { |l| l.chomp.split("\t") } + end + + def cache(mention, actor, a) + sharedInbox = a['endpoints']['sharedInbox'] if a['endpoints'] and a['endpoints']['sharedInbox'] + File.open('cache/people.tsv', 'a') { |f| f.puts "#{mention}\t#{actor}\t#{sharedInbox}" } + end end |