clacks/lib/clacks/object.ex

111 lines
3.0 KiB
Elixir
Raw Normal View History

2019-09-28 22:30:55 +00:00
defmodule Clacks.Object do
2019-09-30 20:52:50 +00:00
require Logger
2019-09-28 22:30:55 +00:00
use Ecto.Schema
2019-09-29 01:57:11 +00:00
import Ecto.Changeset
2019-09-30 20:52:50 +00:00
alias Clacks.Repo
import Ecto.Query
2019-09-28 22:30:55 +00:00
@type t() :: %__MODULE__{}
schema "objects" do
field :data, :map
timestamps()
end
2019-09-29 01:57:11 +00:00
def changeset(%__MODULE__{} = schema, attrs) do
schema
|> cast(attrs, [:data])
|> validate_required([:data])
end
2019-09-30 20:52:50 +00:00
@spec changeset_for_creating(data :: map()) :: Ecto.Changeset.t()
def changeset_for_creating(data) do
changeset(%__MODULE__{}, %{data: data})
end
@spec get_actor(object :: t()) :: Clacks.Actor.t() | nil
def get_actor(object) do
case object.data["actor"] || object.data["attributedTo"] do
nil ->
nil
id when is_binary(id) ->
Clacks.Actor.get_by_ap_id(id)
end
end
2019-09-30 20:52:50 +00:00
@spec get_by_ap_id(
ap_id :: String.t(),
force_refetch :: boolean(),
synthesize_create :: boolean()
) :: t() | nil
2019-09-30 21:02:03 +00:00
def get_by_ap_id(ap_id, force_refetch \\ false, synthesize_create \\ true) do
2019-09-30 20:52:50 +00:00
if force_refetch do
fetch(ap_id, synthesize_create)
else
get_cached_by_ap_id(ap_id) || fetch(ap_id, synthesize_create)
end
end
@spec get_cached_by_ap_id(ap_id :: String.t()) :: t() | nil
2019-09-30 21:02:03 +00:00
def get_cached_by_ap_id(ap_id) do
2019-09-30 20:52:50 +00:00
Repo.one(from o in __MODULE__, where: fragment("?->>'id'", o.data) == ^ap_id)
end
@spec fetch(url :: String.t(), synthesize_create :: boolean(), return :: :object | :activity) ::
2020-04-26 23:28:22 +00:00
t() | Activity.t() | nil
def fetch(url, synthesize_create \\ true, return \\ :object) when is_binary(url) do
case Clacks.ActivityPub.Fetcher.fetch_object(url) do
2019-09-30 20:52:50 +00:00
nil ->
nil
%{"id" => ap_id} = data ->
existing = get_cached_by_ap_id(ap_id)
2019-09-30 20:52:50 +00:00
changeset =
changeset(existing || %__MODULE__{}, %{
data: data
})
case Repo.insert_or_update(changeset) do
{:ok, object} ->
2019-09-30 21:02:03 +00:00
actor = data["actor"] || data["attributedTo"]
_ = Clacks.Actor.get_by_ap_id(actor)
activity =
2021-08-26 13:59:55 +00:00
case Clacks.Activity.get_by_object_ap_id(ap_id, "Create") do
nil ->
if synthesize_create do
create = Clacks.ActivityPub.synthesized_create(data)
changeset =
Clacks.Activity.changeset(%Clacks.Activity{}, %{
data: create,
local: false,
actor: actor
})
{:ok, create} = Repo.insert_or_update(changeset)
%Clacks.Activity{create | object: object}
else
nil
end
%Clacks.Activity{} = activity ->
activity
end
case return do
:object -> object
:activity -> activity
2019-09-30 20:52:50 +00:00
end
{:error, changeset} ->
Logger.error("Couldn't store remote object #{ap_id}: #{inspect(changeset)}")
nil
end
end
end
2019-09-28 22:30:55 +00:00
end