summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpdp8 <pdp8@pdp8.info>2023-05-19 22:50:54 +0200
committerpdp8 <pdp8@pdp8.info>2023-05-19 22:50:54 +0200
commitd2c4d3b49e6b790d14f6dcb94c2ae0641559d2cf (patch)
tree09148e8d81e4686a9c969bb74b3f4f6fc9325f79
parentd523ce8d4fe4e852944331eb0c300d309641c057 (diff)
follow and followers
-rw-r--r--application.rb133
-rwxr-xr-xclient.rb72
-rwxr-xr-xpost.rb45
-rwxr-xr-xsocial33
4 files changed, 126 insertions, 157 deletions
diff --git a/application.rb b/application.rb
index 9e4e3a8..66b47b2 100644
--- a/application.rb
+++ b/application.rb
@@ -47,9 +47,27 @@ class Application
puts input
when "Follow"
File.open(File.join("followers", SecureRandom.uuid + ".json"), "w+") { |f| f.puts input }
- # TODO return accept activity
+ accept = { "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => File.join(SOCIAL_URL + "#accepts", SecureRandom.uuid),
+ "type" => "Accept",
+ "actor" => ACTOR,
+ "object" => JSON.parse(input) }
+ send accept, [accept["object"]["actor"]]
when "Undo"
- puts input
+ o = object["object"]
+ # puts o
+ case o["type"]
+ when "Follow"
+ Dir["followers/*.json"].each do |follower|
+ puts follower
+ # puts JSON.parse(File.read(follower))["actor"]
+ if JSON.parse(File.read(follower))["actor"] == o["actor"]
+ FileUtils.rm follower
+ end
+ end
+ # ordered_collection("followers")
+ end
+ # puts input
else
puts input
end
@@ -66,12 +84,50 @@ class Application
when "/outbox" # receive from client
if auth(env)
- code, response = process input
+ code, response = parse input
+ else
+ code = 403
+ response = "You are not allowed to POST to #{env["REQUEST_URI"]}."
+ end
+
+ when "/follow" # receive from client
+ if auth(env)
+ input.split.each do |mention|
+ actor = actor(mention)
+ follow = { "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => File.join(SOCIAL_URL, "following", SecureRandom.uuid + ".json"),
+ "type" => "Follow",
+ "actor" => ACTOR,
+ "object" => actor }
+ save follow
+ puts(send follow, [actor])
+ code = 200
+ response = "OK"
+ end
else
code = 403
response = "You are not allowed to POST to #{env["REQUEST_URI"]}."
end
+ when "/unfollow" # receive from client
+ if auth(env)
+ input.split.each do |mention|
+ actor = actor(mention)
+ Dir["following/*.json"].each do |f|
+ follow = JSON.parse(File.read(f))
+ puts follow
+ if follow["object"] == actor
+ undo = { "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => File.join(SOCIAL_URL + "#undo", SecureRandom.uuid),
+ "type" => "Undo",
+ "actor" => ACTOR,
+ "object" => follow }
+ send undo, [actor]
+ FileUtils.rm f
+ end
+ end
+ end
+ end
end
when 'GET'
@@ -106,7 +162,7 @@ class Application
[code, { "Content-Type" => type }, [response]]
end
- def process input
+ def parse input
date = Time.now.strftime("%Y-%m-%dT%H:%M:%S")
# TODO media attachments, hashtags
note = {
@@ -173,30 +229,17 @@ class Application
[type, response]
end
- def actor account
- account = account.sub(/^@/, '').chomp
- user, server = account.split("@")
- header = { 'Accept' => "application/jrd+json" }
- uri = URI("https://" + server + "/.well-known/webfinger?resource=acct:#{account}")
- http = Net::HTTP.new(uri.host, uri.port)
- http.use_ssl = true
- request = Net::HTTP::Get.new(uri.request_uri, header)
- response = http.request(request)
- JSON.parse(response.body)["links"].select { |l| l["rel"] == "self" }[0]["href"]
- end
-
- def path object
- object["id"].sub(SOCIAL_URL, '').sub('/', '')
- end
-
- def save object
- path = path object
- FileUtils.mkdir_p File.dirname(path)
- File.open(path, "w+") { |f| f.puts object.to_json }
+ def actor mention
+ mention = mention.sub(/^@/, '').chomp
+ user, server = mention.split("@")
+ get("https://#{server}/.well-known/webfinger?resource=acct:#{mention}",
+ "application/jrd+json")["links"].select { |l|
+ l["rel"] == "self"
+ }[0]["href"]
end
def send object, urls
- puts object, urls
+ # puts object, urls
# https://github.com/mastodon/mastodon/blob/main/app/lib/request.rb
keypair = OpenSSL::PKey::RSA.new(File.read('private.pem'))
responses = []
@@ -227,7 +270,7 @@ class Application
responses << http.request(request)
end
- puts responses
+ # puts responses
responses
end
@@ -245,6 +288,30 @@ class Application
}
end
+ def inbox uri
+ URI(get(uri)["inbox"]).request_uri
+ end
+
+ def get url, accept = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
+ uri = URI(url)
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true
+ header = { 'Accept' => accept }
+ request = Net::HTTP::Get.new(uri.request_uri, header)
+ response = http.request(request)
+ JSON.parse(response.body)
+ end
+
+ def path object
+ object["id"].sub(SOCIAL_URL, '').sub('/', '')
+ end
+
+ def save object
+ path = path object
+ FileUtils.mkdir_p File.dirname(path)
+ File.open(path, "w+") { |f| f.puts object.to_json }
+ end
+
def verify env
# https://github.com/mastodon/mastodon/blob/main/app/controllers/concerns/signature_verification.rb
# TODO verify digest
@@ -274,20 +341,6 @@ class Application
key.verify(OpenSSL::Digest.new('SHA256'), signature, comparison)
end
- def get url
- uri = URI(url)
- http = Net::HTTP.new(uri.host, uri.port)
- http.use_ssl = true
- header = { 'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' }
- request = Net::HTTP::Get.new(uri.request_uri, header)
- response = http.request(request)
- JSON.parse(response.body)
- end
-
- def inbox uri
- URI(get(uri)["inbox"]).request_uri
- end
-
def auth env
auth = Rack::Auth::Basic::Request.new(env)
usr = File.read(".usr").chomp
diff --git a/client.rb b/client.rb
deleted file mode 100755
index 0ef2896..0000000
--- a/client.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env ruby
-# TODO
-# run as service
-# client post md
-# direct from client (key)
-# via server (auth)
-require 'json'
-require 'net/http'
-require 'uri'
-require 'base64'
-require 'securerandom'
-require 'fileutils'
-require 'digest/sha2'
-
-USER = "pdp8"
-WWW_DOMAIN = "pdp8.info"
-WWW_URL = "https://#{WWW_DOMAIN}"
-SOCIAL_DOMAIN = "social.#{WWW_DOMAIN}"
-
-ACCOUNT = "#{USER}@#{SOCIAL_DOMAIN}"
-SOCIAL_URL = "https://#{SOCIAL_DOMAIN}"
-ACTOR = File.join(SOCIAL_URL, USER)
-
-=begin
-MATRIX = "@#{USER}:matrix.#{WWW_DOMAIN}"
-
-post = {
- "@context" => "https://www.w3.org/ns/activitystreams",
- "type" => "Note",
- "content" => ""
-}
-
-def webfinger account
- account = account.sub(/^@/, '').chomp
- user, server = account.split("@")
- header = { 'Accept' => "application/jrd+json" }
- uri = URI("https://" + server + "/.well-known/webfinger?resource=acct:#{account}")
- http = Net::HTTP.new(uri.host, uri.port)
- http.use_ssl = true
- request = Net::HTTP::Get.new(uri.request_uri, header)
- response = http.request(request)
- JSON.parse(response.body)["links"].select { |l| l["rel"] == "self" }[0]["href"]
-end
-
-ARGF.each do |line|
- if /^(To|Cc|Bcc):/.match line
- dest, addresses = line.split(/: */)
- dest = dest.downcase
- post[dest] ||= []
- addresses.split(/, */).each do |add|
- post[dest] << webfinger(add.chomp)
- end
- else
- post["content"] << line
- end
-end
-=end
-
-uri = URI.parse(File.join SOCIAL_URL, "outbox")
-http = Net::HTTP.new(uri.host, uri.port)
-http.use_ssl = true
-header = { 'Content-Type' => 'text/plain' }
-# header = { 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' }
-request = Net::HTTP::Post.new(uri.request_uri, header)
-usr = File.read(".usr").chomp
-pwd = File.read(".pwd").chomp
-request.basic_auth(usr, pwd)
-request.body = File.read ARGV[0]
-
-response = http.request(request)
-# TODO return error if response.code > 400
-puts(response.body, response.code)
diff --git a/post.rb b/post.rb
deleted file mode 100755
index c5e7cd7..0000000
--- a/post.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env ruby
-require 'json'
-require 'time'
-require 'openssl'
-require 'base64'
-require 'net/http'
-require 'uri'
-
-# document = { "a" => 2 } # .to_json
-document = {
- "@context": "https://www.w3.org/ns/activitystreams",
- "type": "Like",
- "actor": "https://example.net/~mallory",
- "to": ["https://hatchat.example/sarah/",
- "https://example.com/peeps/john/"],
- "object": {
- "@context": { "@language": "en" },
- "id": "https://example.org/~alice/note/23",
- "type": "Note",
- "attributedTo": "https://example.org/~alice",
- "content": "I'm a goat"
- }
-}
-date = Time.now.utc.httpdate
-keypair = OpenSSL::PKey::RSA.new(File.read('private.pem'))
-signed_string = "(request-target): post /inbox\nhost: social.pdp8.info\ndate: #{date}"
-signed_string = keypair.sign(OpenSSL::Digest::SHA256.new, signed_string)
-signature = Base64.urlsafe_encode64(signed_string).encode("UTF-8")
-signed_header = 'keyId="https://social.pdp8.info/pdp8",headers="(request-target) host date",signature="' + signature + '"'
-
-uri = URI.parse("https://social.pdp8.info/inbox")
-http = Net::HTTP.new(uri.host, uri.port)
-http.use_ssl = true
-http.verify_mode = OpenSSL::SSL::VERIFY_NONE
-header = {
- 'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
- 'Host' => 'social.pdp8.info',
- 'Date' => date,
- 'Signature' => signed_header,
-}
-request = Net::HTTP::Post.new(uri.request_uri, header)
-request.body = document.to_json
-
-response = http.request(request)
-puts(response.body, response.code)
diff --git a/social b/social
new file mode 100755
index 0000000..2a57153
--- /dev/null
+++ b/social
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+require 'net/http'
+require 'uri'
+
+USER = "pdp8"
+WWW_DOMAIN = "pdp8.info"
+SOCIAL_DOMAIN = "social.#{WWW_DOMAIN}"
+SOCIAL_URL = "https://#{SOCIAL_DOMAIN}"
+
+def post path, body
+ uri = URI.parse(File.join SOCIAL_URL, path)
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true
+ header = { 'Content-Type' => 'text/plain' }
+ request = Net::HTTP::Post.new(uri.request_uri, header)
+ usr = File.read(".usr").chomp
+ pwd = File.read(".pwd").chomp
+ request.basic_auth(usr, pwd)
+ request.body = body
+ response = http.request(request)
+ # TODO return error if response.code > 400
+ puts(response.body, response.code)
+end
+
+# cmd = ARGV.shift
+case ARGV.shift
+when "post"
+ post "outbox", File.read(ARGV[0])
+when "follow"
+ post "follow", ARGV.join(" ")
+when "unfollow"
+ post "unfollow", ARGV.join(" ")
+end