# client-server post "/outbox" do protected! request.body.rewind # in case someone already read it #body = request.body.read date = Time.now.strftime("%Y-%m-%dT%H:%M:%S") # TODO hashtags, replys outbox_path = File.join("public/outbox", date + ".json") object_path = File.join("public/objects", date + ".json") #recipients = ["https://www.w3.org/ns/activitystreams#Public", params[:to]] recipients = [params[:to]] #recipients += Dir[File.join("public/followers", "*.json")].collect { |f| JSON.parse(File.read(f))["actor"] } recipients.delete ACTOR recipients.uniq! 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, object_path), "type" => "Note", "attributedTo" => ACTOR, "inReplyTo" => params[:inReplyTo], "published" => date, "content" => params[:content], "to" => recipients }, "published" => date, "to" => recipients } =begin if /^@/.match body mentions, body = body.split("\n", 2) mentions.split(/, */).each do |m| recipients << actor(m.chomp) end end =end #create["object"]["content"] = body#.lines.select { |l| !l.empty? }.join("
") #create["object"]["to"] += recipients #create["to"] += recipients #p params["to"] #p create File.open(outbox_path, "w+") { |f| f.puts create.to_json } File.open(object_path, "w+") { |f| f.puts create["object"].to_json } #p recipients create["to"].each { |r| send_signed create, r } redirect to(params['redirect']) end post "/archive" do protected! FileUtils.mv params['file'], "archive/" redirect to(params['redirect']) end post "/delete" do protected! FileUtils.rm params['file'] redirect to(params['redirect']) end post "/delete_all" do protected! FileUtils.rm Dir["inbox/*.json"] redirect to("/") end post "/follow/*" do protected! mention = params['splat'][0] actor = actor(mention) follow = { "@context" => "https://www.w3.org/ns/activitystreams", "id" => File.join(SOCIAL_URL, "following", mention + ".json"), "type" => "Follow", "actor" => ACTOR, "object" => actor } send_signed follow, actor redirect to("/") end post "/unfollow/*" do protected! mention = params['splat'][0] actor = actor(mention) following_path = File.join("public", "following", mention + ".json") if File.exists?(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)) } send_signed undo, actor FileUtils.rm following_path redirect to("/") end end post "/login" do session["client"] = true if params["secret"] == File.read(".pwd").chomp redirect to("/") end # private ["/", "/archive"].each do |path| get path, :provides => 'html' do protected! if path == '/' @dir = 'inbox' @alt_dir = '/archive' @alt_name = 'archive' else @dir = path.sub('/','') @alt_dir = '/' @alt_name = 'inbox' end threads erb :index end end helpers do def protected! redirect("/login.html") unless session['client'] end def items nr = 0 files = Dir[File.join(@dir, '*.json')] + Dir['public/objects/*.json'] @items = files.sort.collect do |file| item = JSON.parse(File.read(file)) mention = mention(item['attributedTo']) following_path = File.join('public', 'following', mention + '.json') File.exists?(following_path) ? follow = 'unfollow' : follow = 'follow' nr += 1 { :id => item['id'], :nr => nr, :parent => item['inReplyTo'], :file => file, :actor_url => item['attributedTo'], :mention => mention, :follow => follow, :content => item['content'], :attachment => item['attachment'], :indent => 2, :replies => [] } end.compact @items.last[:nr] = @items.last[:nr] - 2 unless @items.empty? end def threads items @threads = [] @items.each do |i| if i[:parent].nil? or @items.select{|it| it[:id] == i[:parent] }.empty? @threads << i else @items.select{|it| it[:id] == i[:parent] }.each do |it| i[:indent] = it[:indent] + 2 it[:replies] << i end end end end def html item @item = item erb :item end def people File.read('cache/people.tsv').split("\n").collect {|l| l.chomp.split("\t")} end def mention actor person = people.select{|p| p[1] == actor} if person.empty? mention = "#{fetch(actor)["preferredUsername"]}@#{URI(actor).host}" File.open('cache/people.tsv','a'){|f| f.puts "#{mention}\t#{actor}"} mention else person[0][0] end end def actor mention mention = mention.sub(/^@/, '').chomp actors = people.select{|p| p[0] == mention} if actors.empty? user, server = mention.split("@") actor = fetch("https://#{server}/.well-known/webfinger?resource=acct:#{mention}", "application/jrd+json")["links"].select { |l| l["rel"] == "self" }[0]["href"] File.open('cache/people.tsv','a'){|f| f.puts "#{mention}\t#{actor}"} actor else actors[0][1] end end end