# frozen_string_literal: true # client-server post '/' do protected! date = Time.now.strftime('%Y-%m-%dT%H:%M:%S') outbox_path = File.join('public/outbox', "#{date}.json") notes_path = File.join('public/notes', "#{date}.json") recipients = ['https://www.w3.org/ns/activitystreams#Public', params[:to]] recipients += Dir[File.join('public/followers', '*.json')].collect { |f| JSON.parse(File.read(f))['actor'] } recipients.delete ACTOR recipients.uniq! content = [] attachment = [] tag = [] extensions = { image: %w[jpeg png], audio: %w[flac wav mp3 ogg], video: %w[mp4 webm] } params[:content].lines.each do |line| line.chomp! if line.match(/^http/) ext = File.extname(line).sub('.', '') media_type = extensions.select { |_k, v| v.include? ext }.keys[0].to_s + '/' + ext attachment << { 'type' => 'Document', 'mediaType' => media_type, 'url' => line } else tags = line.split(/\s+/).grep(/^#\w+$/) tags.each do |name| href = File.join(SOCIAL_URL, 'tags', name.sub('#', '')) tag << { 'type' => 'Hashtag', 'href' => href, 'name' => name } line.gsub!(name, "") end content << line end end create = { '@context' => 'https://www.w3.org/ns/activitystreams', 'id' => File.join(SOCIAL_URL, outbox_path), 'type' => 'Create', 'actor' => ACTOR, 'object' => { 'id' => File.join(SOCIAL_URL, notes_path), 'type' => 'Note', 'attributedTo' => ACTOR, 'inReplyTo' => params[:inReplyTo], 'published' => date, 'content' => "

\n#{content.join("\n
")}\n

", 'attachment' => attachment, 'tag' => tag, 'to' => recipients }, 'published' => date, 'to' => recipients } File.open(outbox_path, 'w+') { |f| f.puts create.to_json } File.open(notes_path, 'w+') { |f| f.puts create['object'].to_json } tag.each do |t| dir = File.join('public', 'tags', t['name'].sub('#', '')) FileUtils.mkdir_p dir FileUtils.ln_s File.join('/srv/social/', notes_path), dir end # recipients.delete "https://www.w3.org/ns/activitystreams#Public" # recipients.each { |r| send_signed create, r } # send_signed create # , r } outbox create redirect params['redirect'] end post '/archive' do protected! FileUtils.mv params['file'], 'archive/' redirect to(params['redirect']) end post '/delete' do # delete not supported by html forms protected! FileUtils.rm_f(params['file'] || Dir['inbox/*.json']) redirect(params['redirect'] || '/') end post '/follow' do protected! actor, mention = parse_follow params['follow'] follow = { '@context' => 'https://www.w3.org/ns/activitystreams', 'id' => File.join(SOCIAL_URL, 'following', "#{mention}.json"), 'type' => 'Follow', 'actor' => ACTOR, 'object' => actor, 'to' => [actor] } send_signed follow # , actor redirect '/' end post '/unfollow' do protected! actor, mention = parse_follow params['follow'] following_path = File.join('public', 'following', "#{mention}.json") if File.exist?(following_path) undo = { '@context' => 'https://www.w3.org/ns/activitystreams', 'id' => File.join("#{SOCIAL_URL}#undo", SecureRandom.uuid), 'type' => 'Undo', 'actor' => ACTOR, 'object' => JSON.parse(File.read(following_path)), 'to' => [actor] } send_signed undo # , actor FileUtils.rm following_path redirect '/' end end post '/login' do session['client'] = (OpenSSL::Digest::SHA256.base64digest(params['secret']) == File.read('.digest').chomp) redirect '/' end get '/' do redirect '/inbox' end ['/inbox', '/archive', '/outbox'].each do |path| get path, provides: 'html' do protected! @dir = path.sub('/', '') collection = ordered_collection(File.join(SOCIAL_DIR, path, 'note'))['orderedItems'] @threads = [] collection.each do |object| object['indent'] = 0 object['replies'] = [] if object['inReplyTo'].nil? || collection.select { |o| o['id'] == object['inReplyTo'] }.empty? @threads << object else collection.select { |o| o['id'] == object['inReplyTo'] }.each do |o| object['indent'] = o['indent'] + 4 o['replies'] << object end end end erb :collection end end helpers do def protected! halt 403 unless session['client'] end def parse_follow(follow) case follow when /^#/ actor = "https://relay.fedi.buzz/tag/#{follow.sub(/^#/, '')}" mention = follow when /^http/ actor = follow mention = mention actor when /^@*\w+@\w+/ mention = follow actor = actor(follow) return 502 unless actor end [actor, mention] end end