2021-04-22 12:17:56 +02:00
|
|
|
defmodule Mobilizon.Federation.ActivityPub.Actor do
|
|
|
|
@moduledoc """
|
|
|
|
Module to handle ActivityPub Actor interactions
|
|
|
|
"""
|
|
|
|
|
|
|
|
alias Mobilizon.Actors
|
|
|
|
alias Mobilizon.Actors.Actor
|
|
|
|
alias Mobilizon.Federation.ActivityPub.{Fetcher, Relay}
|
|
|
|
alias Mobilizon.Federation.WebFinger
|
|
|
|
alias Mobilizon.Web.Endpoint
|
|
|
|
require Logger
|
|
|
|
import Mobilizon.Federation.ActivityPub.Utils, only: [are_same_origin?: 2]
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Getting an actor from url, eventually creating it if we don't have it locally or if it needs an update
|
|
|
|
"""
|
2021-09-10 11:36:05 +02:00
|
|
|
@spec get_or_fetch_actor_by_url(url :: String.t(), preload :: boolean()) ::
|
|
|
|
{:ok, Actor.t()}
|
|
|
|
| {:error, make_actor_errors}
|
|
|
|
| {:error, :no_internal_relay_actor}
|
|
|
|
| {:error, :url_nil}
|
2021-04-22 12:17:56 +02:00
|
|
|
def get_or_fetch_actor_by_url(url, preload \\ false)
|
|
|
|
|
2021-09-10 11:36:05 +02:00
|
|
|
def get_or_fetch_actor_by_url(nil, _preload), do: {:error, :url_nil}
|
2021-04-22 12:17:56 +02:00
|
|
|
|
|
|
|
def get_or_fetch_actor_by_url("https://www.w3.org/ns/activitystreams#Public", _preload) do
|
2021-09-24 16:46:42 +02:00
|
|
|
%Actor{url: url} = Relay.get_actor()
|
|
|
|
get_or_fetch_actor_by_url(url)
|
2021-04-22 12:17:56 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def get_or_fetch_actor_by_url(url, preload) do
|
2021-11-16 15:43:53 +01:00
|
|
|
Logger.debug("Getting or fetching actor by URL #{url}")
|
|
|
|
|
2021-09-10 11:36:05 +02:00
|
|
|
case Actors.get_actor_by_url(url, preload) do
|
|
|
|
{:ok, %Actor{} = cached_actor} ->
|
2021-09-24 16:46:42 +02:00
|
|
|
if Actors.needs_update?(cached_actor) do
|
2021-11-13 18:45:01 +01:00
|
|
|
__MODULE__.make_actor_from_url(url, preload: preload)
|
2021-09-24 16:46:42 +02:00
|
|
|
else
|
|
|
|
{:ok, cached_actor}
|
2021-04-22 12:17:56 +02:00
|
|
|
end
|
2021-09-10 11:36:05 +02:00
|
|
|
|
|
|
|
{:error, :actor_not_found} ->
|
|
|
|
# For tests, see https://github.com/jjh42/mock#not-supported---mocking-internal-function-calls and Mobilizon.Federation.ActivityPubTest
|
2021-11-13 18:45:01 +01:00
|
|
|
__MODULE__.make_actor_from_url(url, preload: preload)
|
2021-04-22 12:17:56 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-10 11:36:05 +02:00
|
|
|
@type make_actor_errors :: Fetcher.fetch_actor_errors() | :actor_is_local
|
|
|
|
|
2021-04-22 12:17:56 +02:00
|
|
|
@doc """
|
|
|
|
Create an actor locally by its URL (AP ID)
|
|
|
|
"""
|
2021-11-13 18:45:01 +01:00
|
|
|
@spec make_actor_from_url(url :: String.t(), options :: Keyword.t()) ::
|
2021-10-10 16:25:50 +02:00
|
|
|
{:ok, Actor.t()} | {:error, make_actor_errors | Ecto.Changeset.t()}
|
2021-11-13 18:45:01 +01:00
|
|
|
def make_actor_from_url(url, options \\ []) do
|
2021-11-16 15:43:53 +01:00
|
|
|
Logger.debug("Making actor from url #{url}")
|
|
|
|
|
2021-04-22 12:17:56 +02:00
|
|
|
if are_same_origin?(url, Endpoint.url()) do
|
2021-09-10 11:36:05 +02:00
|
|
|
{:error, :actor_is_local}
|
2021-04-22 12:17:56 +02:00
|
|
|
else
|
2021-11-13 18:45:01 +01:00
|
|
|
case Fetcher.fetch_and_prepare_actor_from_url(url, options) do
|
2021-09-10 11:36:05 +02:00
|
|
|
{:ok, data} when is_map(data) ->
|
2021-11-13 18:45:01 +01:00
|
|
|
Actors.upsert_actor(data, Keyword.get(options, :preload, false))
|
2021-04-22 12:17:56 +02:00
|
|
|
|
|
|
|
# Request returned 410
|
|
|
|
{:error, :actor_deleted} ->
|
2021-09-10 11:36:05 +02:00
|
|
|
Logger.info("Actor #{url} was deleted")
|
2021-04-22 12:17:56 +02:00
|
|
|
{:error, :actor_deleted}
|
|
|
|
|
2021-10-10 16:25:50 +02:00
|
|
|
{:error, err} ->
|
2021-09-10 11:36:05 +02:00
|
|
|
{:error, err}
|
2021-04-22 12:17:56 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Find an actor in our local database or call WebFinger to find what's its AP ID is and then fetch it
|
|
|
|
"""
|
2021-09-10 11:36:05 +02:00
|
|
|
@spec find_or_make_actor_from_nickname(nickname :: String.t(), type :: atom() | nil) ::
|
|
|
|
{:ok, Actor.t()} | {:error, make_actor_errors | WebFinger.finger_errors()}
|
2021-04-22 12:17:56 +02:00
|
|
|
def find_or_make_actor_from_nickname(nickname, type \\ nil) do
|
2021-11-16 15:43:53 +01:00
|
|
|
Logger.debug("Finding or making actor from nickname #{nickname}")
|
|
|
|
|
2021-06-16 11:27:14 +02:00
|
|
|
case Actors.get_actor_by_name_with_preload(nickname, type) do
|
|
|
|
%Actor{url: actor_url} = actor ->
|
|
|
|
if Actors.needs_update?(actor) do
|
2021-11-13 18:45:01 +01:00
|
|
|
make_actor_from_url(actor_url, preload: true)
|
2021-06-16 11:27:14 +02:00
|
|
|
else
|
|
|
|
{:ok, actor}
|
|
|
|
end
|
2021-04-22 12:17:56 +02:00
|
|
|
|
|
|
|
nil ->
|
2021-11-13 18:45:01 +01:00
|
|
|
make_actor_from_nickname(nickname, preload: true)
|
2021-04-22 12:17:56 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-10 11:36:05 +02:00
|
|
|
@spec find_or_make_group_from_nickname(nick :: String.t()) ::
|
|
|
|
{:error, make_actor_errors | WebFinger.finger_errors()}
|
2021-04-22 12:17:56 +02:00
|
|
|
def find_or_make_group_from_nickname(nick), do: find_or_make_actor_from_nickname(nick, :Group)
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Create an actor inside our database from username, using WebFinger to find out its AP ID and then fetch it
|
|
|
|
"""
|
2021-09-10 11:36:05 +02:00
|
|
|
@spec make_actor_from_nickname(nickname :: String.t(), preload :: boolean) ::
|
|
|
|
{:ok, Actor.t()} | {:error, make_actor_errors | WebFinger.finger_errors()}
|
2021-06-16 11:27:14 +02:00
|
|
|
def make_actor_from_nickname(nickname, preload \\ false) do
|
2021-11-16 15:43:53 +01:00
|
|
|
Logger.debug("Fingering actor from nickname #{nickname}")
|
|
|
|
|
2021-04-22 12:17:56 +02:00
|
|
|
case WebFinger.finger(nickname) do
|
|
|
|
{:ok, url} when is_binary(url) ->
|
2021-11-16 15:43:53 +01:00
|
|
|
Logger.debug("Matched #{nickname} to URL #{url}, now making actor")
|
2021-11-13 18:45:01 +01:00
|
|
|
make_actor_from_url(url, preload: preload)
|
2021-04-22 12:17:56 +02:00
|
|
|
|
2021-09-10 11:36:05 +02:00
|
|
|
{:error, e} ->
|
|
|
|
{:error, e}
|
2021-04-22 12:17:56 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|