1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# frozen_string_literal: true
# client-server
post '/' do
protected!
Time.now.strftime('%Y-%m-%dT%H:%M:%S.%N')
recipients = public
recipients << params[:to]
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, "<a href='#{href}' class='mention hashtag' rel='tag'>#{name}</a>")
end
content << line
end
end
object = {
'type' => 'Note',
'attributedTo' => ACTOR,
'inReplyTo' => params[:inReplyTo],
'content' => "<p>\n#{content.join("\n<br>")}\n</p>",
'attachment' => attachment,
'tag' => tag,
'to' => recipients
}
outbox 'Create', object, recipients, true
redirect(params['anchor'] || '/inbox')
end
post '/share' do
protected!
selection(params).each { |f| FileUtils.mv f, f.sub(%r{/inbox/}, '/shared/') }
outbox 'Announce', params['id'], public
redirect(params['anchor'] || '/inbox')
end
post '/delete' do
protected!
selection(params).each { |f| FileUtils.rm f }
redirect(params['anchor'] || '/inbox')
end
post '/follow' do
protected!
actor, = parse_follow params['follow']
outbox 'Follow', actor, [actor]
redirect(params['anchor'] || '/inbox')
end
post '/unfollow' do
protected!
actor, mention = parse_follow params['follow']
following_path = File.join(FOLLOWING_DIR, "#{mention}.json")
if File.exist?(following_path)
outbox 'Undo', JSON.parse(File.read(following_path)), [actor]
FileUtils.rm_f following_path
redirect(params['anchor'] || '/inbox')
end
end
post '/login' do
session['client'] = (OpenSSL::Digest::SHA256.base64digest(params['secret']) == File.read('.digest').chomp)
redirect '/inbox'
end
get '/' do
protected!
redirect '/inbox'
end
['/inbox', '/shared', '/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_with_index do |object, _idx|
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'] + 2
o['replies'] << object
end
end
end
erb :collection
end
end
helpers do
def protected!
halt 403 unless session['client']
end
def selection(params)
selection = Dir[File.join(SOCIAL_DIR, params['dir'], '*', '*.json')]
params['id'] ? selection.select { |f| JSON.parse(File.read(f))['id'] == params['id'] } : selection
end
def public
recipients = ['https://www.w3.org/ns/activitystreams#Public']
recipients += Dir[File.join(FOLLOWERS, '*.json')].collect { |f| JSON.parse(File.read(f))['actor'] }
recipients.delete ACTOR
recipients.uniq
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
|