167 lines
4.4 KiB
Elixir
167 lines
4.4 KiB
Elixir
defmodule Postland.Follows do
|
|
use Phoenix.VerifiedRoutes, endpoint: PostlandWeb.Endpoint, router: PostlandWeb.Router
|
|
|
|
import Ecto.Query, warn: false
|
|
|
|
alias Postland.Accounts
|
|
alias Postland.Activity
|
|
alias Postland.Follow
|
|
alias Postland.Repo
|
|
alias ActivityPub.Headers
|
|
|
|
alias Ecto.Multi
|
|
|
|
def all_following() do
|
|
my_actor_id = Postland.my_actor_id()
|
|
|
|
from(f in Follow, where: f.follower == ^my_actor_id, where: not is_nil(f.confirmed_at))
|
|
|> Repo.all()
|
|
end
|
|
|
|
def all_followers() do
|
|
my_actor_id = Postland.my_actor_id()
|
|
|
|
from(f in Follow, where: f.followee == ^my_actor_id, where: not is_nil(f.confirmed_at))
|
|
|> Repo.all()
|
|
end
|
|
|
|
def record_and_send_acceptance(request) do
|
|
Multi.new()
|
|
|> Multi.run(:confirm_timestamp, fn _data, _repo -> confirm_request(request) end)
|
|
|> Multi.run(:send_acceptance, fn _data, _repo ->
|
|
send_acceptance(request)
|
|
end)
|
|
|> Repo.transaction()
|
|
end
|
|
|
|
def send_acceptance(request) do
|
|
{:ok, actor} = ActivityPub.fetch_actor(request.follower)
|
|
inbox = Map.get(actor, "inbox")
|
|
|
|
case get_follow_activity(request.follower) do
|
|
nil ->
|
|
{:error, "could not find Follow activity to Accept"}
|
|
|
|
follow_activity ->
|
|
body =
|
|
%{
|
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
|
"type" => "Accept",
|
|
"actor" => Postland.my_actor_id(),
|
|
"object" => %{
|
|
"actor" => request.follower,
|
|
"id" => Map.get(follow_activity.data, "id"),
|
|
"object" => Postland.my_actor_id(),
|
|
"type" => "Follow"
|
|
}
|
|
}
|
|
|> Jason.encode!()
|
|
|
|
headers =
|
|
Headers.signing_headers(
|
|
"POST",
|
|
inbox,
|
|
body,
|
|
Postland.my_actor_id(),
|
|
Accounts.solo_user().private_key
|
|
)
|
|
|
|
Req.post(inbox, headers: headers, body: body)
|
|
end
|
|
end
|
|
|
|
def get_follow_activity(follower) do
|
|
from(a in Activity,
|
|
where: a.type == "Follow",
|
|
where: a.actor_id == ^follower,
|
|
limit: 1,
|
|
order_by: [desc: :inserted_at]
|
|
)
|
|
|> Repo.one()
|
|
end
|
|
|
|
def record_and_send_follow_request(to_actor_id) do
|
|
Multi.new()
|
|
|> Multi.run(:follow_record, fn _data, _repo -> record_outbound_request(to_actor_id) end)
|
|
|> Multi.run(:send_request, fn _data, _repo ->
|
|
send_follow_request(to_actor_id)
|
|
end)
|
|
|> Repo.transaction()
|
|
|> case do
|
|
{:ok, %{follow_record: follow_record}} = result ->
|
|
Phoenix.PubSub.broadcast(
|
|
Postland.PubSub,
|
|
"follows:#{to_actor_id}",
|
|
{:update, follow_record}
|
|
)
|
|
|
|
result
|
|
|
|
other ->
|
|
other
|
|
end
|
|
end
|
|
|
|
def send_follow_request(to_actor_id) do
|
|
encoded_followee = Base.url_encode64(to_actor_id, padding: false)
|
|
encoded_follower = Base.url_encode64(Postland.my_actor_id(), padding: false)
|
|
|
|
{:ok, actor} = ActivityPub.fetch_actor(to_actor_id)
|
|
inbox = Map.get(actor, "inbox")
|
|
|
|
follow_request =
|
|
%{
|
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
|
"id" => url(~p"/follows/#{encoded_followee}/#{encoded_follower}"),
|
|
"type" => "Follow",
|
|
"actor" => Postland.my_actor_id(),
|
|
"object" => to_actor_id
|
|
}
|
|
|> Jason.encode!()
|
|
|
|
headers =
|
|
Headers.signing_headers(
|
|
"POST",
|
|
inbox,
|
|
follow_request,
|
|
Postland.my_actor_id(),
|
|
Accounts.solo_user().private_key
|
|
)
|
|
|
|
Req.post(inbox, headers: headers, body: follow_request)
|
|
end
|
|
|
|
def get(follower, followee) do
|
|
from(f in Follow, where: f.followee == ^followee, where: f.follower == ^follower)
|
|
|> Repo.one()
|
|
end
|
|
|
|
def pending_inbound_requests() do
|
|
my_actor_id = Postland.my_actor_id()
|
|
|
|
from(f in Follow, where: f.followee == ^my_actor_id, where: is_nil(f.confirmed_at))
|
|
|> Repo.all()
|
|
end
|
|
|
|
def record_outbound_request(to_actor_id) do
|
|
Postland.my_actor_id()
|
|
|> Follow.changeset(to_actor_id)
|
|
|> Repo.insert(conflict_target: [:followee, :follower], on_conflict: :nothing)
|
|
end
|
|
|
|
def record_inbound_request(from_actor_id) do
|
|
from_actor_id
|
|
|> Follow.changeset(Postland.my_actor_id())
|
|
|> Repo.insert(conflict_target: [:followee, :follower], on_conflict: :nothing)
|
|
end
|
|
|
|
def confirm_request(request) do
|
|
request
|
|
|> Follow.confirm_changeset()
|
|
|> Repo.update()
|
|
|> case do
|
|
{:ok, follow} ->
|
|
Phoenix.PubSub.broadcast(Postland.PubSub, "follows:#{follow.followee}", {:update, follow})
|
|
end
|
|
end
|
|
end
|