181 lines
4.4 KiB
Elixir
181 lines
4.4 KiB
Elixir
defmodule Postland.Activities do
|
|
use Phoenix.VerifiedRoutes, endpoint: PostlandWeb.Endpoint, router: PostlandWeb.Router
|
|
|
|
require Logger
|
|
|
|
alias Ecto.Multi
|
|
|
|
alias Postland.Activity
|
|
alias Postland.Repo
|
|
alias Postland.Follows
|
|
|
|
def record_markdown_post(markdown) do
|
|
id = Ecto.UUID.autogenerate()
|
|
html = Earmark.as_html!(markdown)
|
|
|
|
activity =
|
|
%{
|
|
"@context" => [
|
|
"https://www.w3.org/ns/activitystreams",
|
|
%{
|
|
"ostatus" => "http://ostatus.org#",
|
|
"atomUri" => "ostatus:atomUri",
|
|
"inReplyToAtomUri" => "ostatus:inReplyToAtomUri",
|
|
"conversation" => "ostatus:conversation",
|
|
"sensitive" => "as:sensitive",
|
|
"toot" => "http://joinmastodon.org/ns#",
|
|
"votersCount" => "toot:votersCount"
|
|
}
|
|
],
|
|
"id" => url(~p"/activities/#{id}"),
|
|
"type" => "Create",
|
|
"actor" => Postland.my_actor_id(),
|
|
"object" => %{
|
|
"id" => url(~p"/activities/#{id}"),
|
|
"type" => "Note",
|
|
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
|
"attributedTo" => Postland.my_actor_id(),
|
|
"mediaType" => "text/html",
|
|
"content" => html,
|
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
|
"cc" => [
|
|
"#{Postland.my_actor_id()}/followers"
|
|
]
|
|
}
|
|
}
|
|
|
|
activity
|
|
|> Postland.Activity.changeset()
|
|
|> record_create_activity()
|
|
|> broadcast_to_followers(activity)
|
|
end
|
|
|
|
def broadcast_to_followers({:ok, _} = result, activity) do
|
|
my_actor_id = Postland.my_actor_id()
|
|
private_key = Postland.Accounts.solo_user().private_key
|
|
|
|
Postland.Follows.all_followers()
|
|
|> Enum.map(fn %{follower: follower} ->
|
|
inbox = Postland.Actors.inbox(follower)
|
|
dbg(follower)
|
|
dbg(inbox)
|
|
|
|
case inbox do
|
|
nil ->
|
|
:ok
|
|
|
|
inbox ->
|
|
ActivityPub.post_activity(
|
|
my_actor_id,
|
|
private_key,
|
|
inbox,
|
|
activity
|
|
)
|
|
end
|
|
end)
|
|
|
|
result
|
|
end
|
|
|
|
def broadcast_to_followers(result, _activity), do: result
|
|
|
|
def record_create_activity(changeset) do
|
|
Multi.new()
|
|
|> Multi.insert(:activity, changeset)
|
|
|> record_objects(changeset)
|
|
|> Repo.transaction()
|
|
end
|
|
|
|
def record_objects(multi, activity_changeset) do
|
|
activity_changeset
|
|
|> Ecto.Changeset.get_field(:data)
|
|
|> Map.get("object")
|
|
|> List.wrap()
|
|
|> Enum.group_by(fn object ->
|
|
{Map.get(object, "id"), Map.get(object, "type")}
|
|
end)
|
|
|> Enum.reduce(multi, fn {{id, type}, note_as_list_of_types}, multi ->
|
|
Multi.insert(
|
|
multi,
|
|
"#{id}.#{type}",
|
|
Postland.Object.changeset(%{
|
|
"id" => id,
|
|
"type" => type,
|
|
"items" => note_as_list_of_types
|
|
})
|
|
)
|
|
end)
|
|
end
|
|
|
|
def process_activity(params) do
|
|
case record_activity(params) do
|
|
{:ok, activity} ->
|
|
cause_effects(activity)
|
|
|
|
other ->
|
|
other
|
|
end
|
|
end
|
|
|
|
def record_activity(params) do
|
|
params
|
|
|> Activity.changeset()
|
|
|> Repo.insert()
|
|
end
|
|
|
|
# TODO: add effects for CRUD notes
|
|
|
|
def cause_effects(
|
|
%Activity{type: "Accept", data: %{"object" => %{"type" => "Follow", "id" => follow_id}}} =
|
|
activity
|
|
) do
|
|
pattern = ~r|/follows/([^/]+)|
|
|
|
|
actor_id =
|
|
case Regex.run(pattern, follow_id) do
|
|
[_, encoded_actor_id] ->
|
|
Base.url_decode64!(encoded_actor_id, padding: false)
|
|
|
|
_other ->
|
|
nil
|
|
end
|
|
|
|
case Follows.get(Postland.my_actor_id(), actor_id) do
|
|
nil ->
|
|
Logger.warning("Got accept for a follow we don't have in the db: #{actor_id}")
|
|
|
|
{:ok, activity}
|
|
|
|
request ->
|
|
Follows.confirm_request(request)
|
|
end
|
|
|
|
{:ok, activity}
|
|
end
|
|
|
|
def cause_effects(%Activity{type: "Create"} = activity) do
|
|
changeset = Activity.changeset(activity.data)
|
|
|
|
Multi.new()
|
|
|> record_objects(changeset)
|
|
|> Repo.transaction()
|
|
end
|
|
|
|
def cause_effects(
|
|
%Activity{actor_id: actor_id, type: "Follow", data: %{"object" => object}} = activity
|
|
) do
|
|
if object == Postland.my_actor_id() do
|
|
case Postland.Follows.record_inbound_request(actor_id) do
|
|
{:ok, _follow} ->
|
|
{:ok, activity}
|
|
|
|
other ->
|
|
other
|
|
end
|
|
else
|
|
{:ok, activity}
|
|
end
|
|
end
|
|
|
|
def cause_effects(activity), do: {:ok, activity}
|
|
end
|