From 3a87028b34c969744f6c63ba73947cfa32156e7d Mon Sep 17 00:00:00 2001 From: pdp8 Date: Sat, 9 Sep 2023 16:02:50 +0200 Subject: update and clean-inbox added, no outbox send for delete, recursive create for objects with activities (lemmy) --- application.rb | 10 +++------ clean-inbox | 3 +++ client.rb | 23 ++----------------- create.rb | 2 +- helpers.rb | 9 +++++--- server.rb | 71 +++++++++++++++++++++++++++++++++++++++++----------------- update | 3 +++ watch | 3 ++- 8 files changed, 71 insertions(+), 53 deletions(-) create mode 100755 clean-inbox create mode 100755 update diff --git a/application.rb b/application.rb index a03d98c..5236685 100644 --- a/application.rb +++ b/application.rb @@ -1,10 +1,8 @@ -# frozen_string_literal: true - require 'uri' require 'base64' require 'digest/sha2' +require 'time' require 'rack/protection' -# require 'rack/contrib' require 'sinatra' SOCIAL_DIR = '/srv/social/' @@ -25,15 +23,13 @@ INBOX = { dir: File.join(SOCIAL_DIR, 'inbox') } OUTBOX = { dir: File.join(SOCIAL_DIR, 'outbox'), url: File.join(SOCIAL_URL, 'outbox') } TAGS = { dir: File.join(PUBLIC_DIR, 'tags'), url: File.join(SOCIAL_URL, 'tags') } FOLLOWERS_URL = 'https://social.pdp8.info/followers' +VISITED = File.join(INBOX[:dir], 'visited') +ACTIVITIES = %i[create announce follow accept undo delete like update move] CONTENT_TYPE = 'application/activity+json' # CONTENT_TYPE = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' use Rack::Protection -# use Rack::MailExceptions do |mail| -# mail.to 'info@pdp8.info' -# mail.smtp false -# end enable :sessions set :environment, :production diff --git a/clean-inbox b/clean-inbox new file mode 100755 index 0000000..b623890 --- /dev/null +++ b/clean-inbox @@ -0,0 +1,3 @@ +#!/usr/bin/env nu +[create announce undo delete like update move] | each { |a| glob $'/srv/social/inbox/($a)/*.json' | each {|f| rm $f } } + diff --git a/client.rb b/client.rb index 61d1ac3..ca5a338 100644 --- a/client.rb +++ b/client.rb @@ -10,27 +10,8 @@ post '/delete' do protected! params['id'].each do |id| file = find_file id - halt 404 unless file - # if file.match(%r{outbox/}) # find/delete activity - - %w[inbox outbox].each do |box| - Dir[File.join box, 'announce', '*.json'].each do |announce_file| - announce = JSON.load_file(announce_file) - next unless announce['object']['id'] == id - - outbox 'Undo', announce, announce['to'] - FileUtils.rm(announce_file) - end - Dir[File.join box, 'create', '*.json'].each do |create_file| - create = JSON.load_file(create_file) - next unless create['object']['id'] == id - - object = JSON.load_file(file) - outbox 'Delete', object, object['to'] - FileUtils.rm(create_file) - end - end - FileUtils.rm(file) if File.exist? file + halt 404 unless file and File.exist?(file) + FileUtils.rm(file) end 200 end diff --git a/create.rb b/create.rb index 59ef726..12d9a27 100644 --- a/create.rb +++ b/create.rb @@ -87,7 +87,7 @@ post '/create' do # TODO p 'outbox' jj object - outbox 'Create', object, to + # outbox 'Create', object, to 200 end diff --git a/helpers.rb b/helpers.rb index 41dcc33..75221f8 100644 --- a/helpers.rb +++ b/helpers.rb @@ -1,5 +1,10 @@ require 'English' helpers do + def save_item(item, path) + FileUtils.mkdir_p File.dirname(path) + File.open(path, 'w+') { |f| f.puts item.to_json } + end + # add date and id, save def save_activity(activity, box) date = Time.now.utc.iso8601 @@ -24,9 +29,7 @@ helpers do def save_object(object, box) object = fetch(object) if object.is_a? String and object.match(/^http/) return unless object and object['type'] != 'Person' - # File.open(File.join(INBOX[:dir]), 'visited', 'w+').open { |f| f.puts object['id'] } - return if box == INBOX and object['id'] and File.readlines(File.join(INBOX[:dir], 'visited'), - chomp: true).include? object['id'] + return if box == INBOX and object['id'] and File.readlines(VISITED, chomp: true).include? object['id'] object['@context'] = 'https://www.w3.org/ns/activitystreams' if object['attributedTo'] diff --git a/server.rb b/server.rb index 7319efa..68c51e1 100644 --- a/server.rb +++ b/server.rb @@ -2,24 +2,16 @@ post '/inbox' do request.body.rewind # in case someone already read it @body = request.body.read - halt 400 if @body.empty? begin @activity = JSON.parse @body - rescue StandardError + rescue StandardError => e p @body halt 400 end # deleted actors return 403 => verification error halt 200 if @activity['type'] == 'Delete' and @activity['actor'] == @activity['object'] # verify! # pixelfed sends unsigned activities??? - type = @activity['type'].downcase.to_sym - save_activity(@activity, INBOX) # unless %i[create announce].include? type - if %i[create announce follow accept undo delete like update move].include? type - send(type) - else - p "Unknown activity #{type}:" - jj @activity - end + handle_activity 200 end @@ -66,12 +58,7 @@ helpers do def create @count ||= 0 @object ||= @activity['object'] - - @object = if @object['type'] == 'Like' # lemmy likes - save_object @object['object'], INBOX - else - save_object @object, INBOX - end + save_inbox_object return unless @object and @object['inReplyTo'] and @count < 5 # recursive thread download @@ -81,11 +68,14 @@ helpers do end def announce + @object ||= @activity['object'] + @object = fetch(@object) if @object.is_a? String and @object.match(/^http/) + @object['announce'] = @activity['actor'] if @object create end def like - create + announce end def follow @@ -94,8 +84,13 @@ helpers do end def accept - halt 501 unless @activity['object']['type'] == 'Follow' - update_collection FOLLOWING, @activity['object']['object'] + if @activity['object']['type'] == 'Follow' + update_collection FOLLOWING, @activity['object']['object'] + else + p "Cannot accept @activity['object']['type']" + jj @activity + halt 501 + end end def undo @@ -106,12 +101,15 @@ helpers do file = find_file @activity['object']['object'] FileUtils.rm(file) if file else + p "Cannot undo @activity['object']['type']" + jj @activity halt 501 end end def update - FileUtils.rm(find_file(@activity['object']['id'])) + file = find_file(@activity['object']['id']) + FileUtils.rm(file) if file create end @@ -124,6 +122,39 @@ helpers do outbox 'Follow', @activity['target'], [@activity['target']] if @activity['actor'] == @activity['object'] end + def handle_activity + type = @activity['type'].downcase.to_sym + save_item @activity, File.join(INBOX[:dir], @activity['type'].downcase, activity_name) + if ACTIVITIES.include? type + send(type) + else + p "Unknown activity #{type}:" + jj @activity + end + end + + def activity_name + @activity['published'] ? "#{@activity['published']}_#{mention(@activity['actor'])}.json" : "_#{Time.now.utc.iso8601}_#{mention(@activity['actor'])}.json" + end + + def save_inbox_object + @object = fetch(@object) if @object.is_a? String and @object.match(/^http/) + if @object['type'] and ACTIVITIES.include? @object['type'].downcase.to_sym + @activity = @object + handle_activity + return + end + # @object = @object['object'] if @object['type'] == 'Like' # lemmy likes + return unless @object and @object['type'] != 'Person' + + if @activity['type'] != 'Update' && (@object['id'] and File.readlines(VISITED, chomp: true).include? @object['id']) + return + end + + save_item @object, File.join(INBOX[:dir], 'object', @object['type'].downcase, activity_name) + File.open(File.join(INBOX[:dir], 'visited'), 'a+') { |f| f.puts @object['id'] } + end + # https://github.com/mastodon/mastodon/blob/main/app/controllers/concerns/signature_verification.rb def verify! # digest diff --git a/update b/update new file mode 100755 index 0000000..24eebb5 --- /dev/null +++ b/update @@ -0,0 +1,3 @@ +#!/bin/sh +rsync -a --exclude='.git/' --exclude='watch' --exclude='generate-digest.rb' --exclude='update '--exclude='.gitignore' --exclude='TODO' --filter=":- .gitignore" ./ /srv/social/ +sudo systemctl restart social.service diff --git a/watch b/watch index 41ad68b..4c020a9 100755 --- a/watch +++ b/watch @@ -1,5 +1,6 @@ #!/bin/sh while inotifywait -qq -r ./ -e create,delete,modify; do - rsync -a --exclude='.git/' --exclude='watch' --exclude='generate-digest.rb' --exclude='.gitignore' --exclude='TODO' --filter=":- .gitignore" ./ /srv/social/ + update + #rsync -a --exclude='.git/' --exclude='watch' --exclude='generate-digest.rb' --exclude='.gitignore' --exclude='TODO' --filter=":- .gitignore" ./ /srv/social/ sudo systemctl restart social.service done -- cgit v1.2.3