# 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, "#{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