2018-12-24 12:34:45 +00:00
|
|
|
# Portions of this file are derived from Pleroma:
|
|
|
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social>
|
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
2018-12-27 10:24:04 +00:00
|
|
|
# Upstream: https://git.pleroma.social/pleroma/pleroma/blob/develop/lib/pleroma/web/activity_pub/activity_pub.ex
|
2018-12-24 12:34:45 +00:00
|
|
|
|
2020-01-22 01:14:42 +00:00
|
|
|
defmodule Mobilizon.Federation.ActivityPub do
|
2018-06-14 16:15:27 +00:00
|
|
|
@moduledoc """
|
2020-01-22 01:14:42 +00:00
|
|
|
The ActivityPub context.
|
2018-06-14 16:15:27 +00:00
|
|
|
"""
|
|
|
|
|
2020-01-22 01:14:42 +00:00
|
|
|
import Mobilizon.Federation.ActivityPub.Utils
|
2018-05-17 09:32:23 +00:00
|
|
|
|
2020-02-18 07:57:00 +00:00
|
|
|
alias Mobilizon.{
|
|
|
|
Actors,
|
2020-07-09 15:24:28 +00:00
|
|
|
Discussions,
|
2020-02-18 07:57:00 +00:00
|
|
|
Events,
|
2020-10-19 07:32:37 +00:00
|
|
|
Posts,
|
2021-09-28 17:40:37 +00:00
|
|
|
Resources
|
2020-02-18 07:57:00 +00:00
|
|
|
}
|
|
|
|
|
2021-09-28 17:40:37 +00:00
|
|
|
alias Mobilizon.Actors.Actor
|
2020-07-09 15:24:28 +00:00
|
|
|
alias Mobilizon.Discussions.Comment
|
2021-09-28 17:40:37 +00:00
|
|
|
alias Mobilizon.Events.Event
|
2019-11-15 17:36:47 +00:00
|
|
|
alias Mobilizon.Tombstone
|
2020-01-22 01:14:42 +00:00
|
|
|
|
|
|
|
alias Mobilizon.Federation.ActivityPub.{
|
|
|
|
Activity,
|
2020-07-09 15:24:28 +00:00
|
|
|
Fetcher,
|
|
|
|
Preloader,
|
2021-09-28 17:40:37 +00:00
|
|
|
Relay
|
2020-01-22 01:14:42 +00:00
|
|
|
}
|
|
|
|
|
2021-04-22 10:17:56 +00:00
|
|
|
alias Mobilizon.Federation.ActivityStream.Convertible
|
2020-01-22 01:14:42 +00:00
|
|
|
|
2020-07-09 15:24:28 +00:00
|
|
|
alias Mobilizon.Storage.Page
|
2020-01-26 20:11:16 +00:00
|
|
|
|
2020-01-28 18:18:33 +00:00
|
|
|
alias Mobilizon.Web.Endpoint
|
2018-05-17 09:32:23 +00:00
|
|
|
|
2018-11-12 08:05:31 +00:00
|
|
|
require Logger
|
2018-05-17 09:32:23 +00:00
|
|
|
|
2020-09-02 06:59:59 +00:00
|
|
|
@public_ap_adress "https://www.w3.org/ns/activitystreams#Public"
|
|
|
|
|
2018-11-12 17:17:53 +00:00
|
|
|
@doc """
|
|
|
|
Fetch an object from an URL, from our local database of events and comments, then eventually remote
|
|
|
|
"""
|
2019-02-21 17:11:49 +00:00
|
|
|
# TODO: Make database calls parallel
|
2020-07-09 15:24:28 +00:00
|
|
|
@spec fetch_object_from_url(String.t(), Keyword.t()) ::
|
2021-09-24 14:46:42 +00:00
|
|
|
{:ok, struct()} | {:ok, atom(), struct()} | {:error, any()}
|
2020-07-09 15:24:28 +00:00
|
|
|
def fetch_object_from_url(url, options \\ []) do
|
2018-12-14 16:41:55 +00:00
|
|
|
Logger.info("Fetching object from url #{url}")
|
|
|
|
|
2021-09-28 17:40:37 +00:00
|
|
|
if String.starts_with?(url, "http") do
|
|
|
|
with {:existing, nil} <-
|
|
|
|
{:existing, Tombstone.find_tombstone(url)},
|
|
|
|
{:existing, nil} <- {:existing, Events.get_event_by_url(url)},
|
|
|
|
{:existing, nil} <-
|
|
|
|
{:existing, Discussions.get_discussion_by_url(url)},
|
|
|
|
{:existing, nil} <- {:existing, Discussions.get_comment_from_url(url)},
|
|
|
|
{:existing, nil} <- {:existing, Resources.get_resource_by_url(url)},
|
|
|
|
{:existing, nil} <- {:existing, Posts.get_post_by_url(url)},
|
|
|
|
{:existing, nil} <-
|
|
|
|
{:existing, Actors.get_actor_by_url_2(url)},
|
|
|
|
{:existing, nil} <- {:existing, Actors.get_member_by_url(url)},
|
|
|
|
:ok <- Logger.info("Data for URL not found anywhere, going to fetch it"),
|
|
|
|
{:ok, _activity, entity} <- Fetcher.fetch_and_create(url, options) do
|
|
|
|
Logger.debug("Going to preload the new entity")
|
|
|
|
Preloader.maybe_preload(entity)
|
|
|
|
else
|
|
|
|
{:existing, entity} ->
|
|
|
|
handle_existing_entity(url, entity, options)
|
2021-04-28 16:06:17 +00:00
|
|
|
|
2021-09-28 17:40:37 +00:00
|
|
|
{:error, e} ->
|
2023-08-02 07:59:09 +00:00
|
|
|
Logger.warning("Something failed while fetching url #{url} #{inspect(e)}")
|
2021-09-28 17:40:37 +00:00
|
|
|
{:error, e}
|
|
|
|
end
|
|
|
|
else
|
|
|
|
{:error, :url_not_http}
|
2021-04-21 16:57:23 +00:00
|
|
|
end
|
|
|
|
end
|
2020-07-09 15:24:28 +00:00
|
|
|
|
2021-04-21 16:57:23 +00:00
|
|
|
@spec handle_existing_entity(String.t(), struct(), Keyword.t()) ::
|
|
|
|
{:ok, struct()}
|
2021-09-24 14:46:42 +00:00
|
|
|
| {:ok, atom(), struct()}
|
2021-04-21 16:57:23 +00:00
|
|
|
| {:error, String.t(), struct()}
|
|
|
|
| {:error, String.t()}
|
|
|
|
defp handle_existing_entity(url, entity, options) do
|
|
|
|
Logger.debug("Entity is already existing")
|
|
|
|
Logger.debug("Going to preload an existing entity")
|
2020-08-27 09:53:24 +00:00
|
|
|
|
2021-04-21 16:57:23 +00:00
|
|
|
case refresh_entity(url, entity, options) do
|
|
|
|
{:ok, entity} ->
|
|
|
|
Preloader.maybe_preload(entity)
|
|
|
|
|
|
|
|
{:error, status, entity} ->
|
|
|
|
{:ok, entity} = Preloader.maybe_preload(entity)
|
|
|
|
{:error, status, entity}
|
|
|
|
|
2021-09-24 14:46:42 +00:00
|
|
|
{:error, err} ->
|
|
|
|
{:error, err}
|
2021-04-21 16:57:23 +00:00
|
|
|
end
|
|
|
|
end
|
2020-10-19 07:32:37 +00:00
|
|
|
|
2021-04-21 16:57:23 +00:00
|
|
|
@spec refresh_entity(String.t(), struct(), Keyword.t()) ::
|
2021-09-28 17:40:37 +00:00
|
|
|
{:ok, struct()} | {:error, atom(), struct()} | {:error, atom()}
|
2021-04-21 16:57:23 +00:00
|
|
|
defp refresh_entity(url, entity, options) do
|
|
|
|
force_fetch = Keyword.get(options, :force, false)
|
2019-07-30 08:35:29 +00:00
|
|
|
|
2021-04-21 16:57:23 +00:00
|
|
|
if force_fetch and not are_same_origin?(url, Endpoint.url()) do
|
|
|
|
Logger.debug("Entity is external and we want a force fetch")
|
2020-09-02 06:59:59 +00:00
|
|
|
|
2021-04-21 16:57:23 +00:00
|
|
|
case Fetcher.fetch_and_update(url, options) do
|
|
|
|
{:ok, _activity, entity} ->
|
|
|
|
{:ok, entity}
|
2020-09-02 06:59:59 +00:00
|
|
|
|
2021-09-10 09:36:05 +00:00
|
|
|
{:error, :http_gone} ->
|
|
|
|
{:error, :http_gone, entity}
|
2019-07-30 08:35:29 +00:00
|
|
|
|
2021-09-10 09:36:05 +00:00
|
|
|
{:error, :http_not_found} ->
|
|
|
|
{:error, :http_not_found, entity}
|
2019-12-03 10:29:51 +00:00
|
|
|
|
2021-09-24 14:46:42 +00:00
|
|
|
{:error, err} ->
|
|
|
|
{:error, err}
|
|
|
|
end
|
2018-11-12 08:05:31 +00:00
|
|
|
else
|
2021-09-28 17:40:37 +00:00
|
|
|
{:ok, entity}
|
2019-07-30 14:40:59 +00:00
|
|
|
end
|
2018-05-17 09:32:23 +00:00
|
|
|
end
|
|
|
|
|
2018-11-12 17:17:53 +00:00
|
|
|
@doc """
|
|
|
|
Return all public activities (events & comments) for an actor
|
|
|
|
"""
|
2021-09-10 09:27:59 +00:00
|
|
|
@spec fetch_public_activities_for_actor(Actor.t(), pos_integer(), pos_integer()) :: map()
|
2020-08-31 15:26:08 +00:00
|
|
|
def fetch_public_activities_for_actor(%Actor{id: actor_id} = actor, page \\ 1, limit \\ 10) do
|
|
|
|
%Actor{id: relay_actor_id} = Relay.get_actor()
|
|
|
|
|
2020-07-09 15:24:28 +00:00
|
|
|
%Page{total: total_events, elements: events} =
|
2020-08-31 15:26:08 +00:00
|
|
|
if actor_id == relay_actor_id do
|
|
|
|
Events.list_public_local_events(page, limit)
|
|
|
|
else
|
|
|
|
Events.list_public_events_for_actor(actor, page, limit)
|
|
|
|
end
|
2020-02-18 07:57:00 +00:00
|
|
|
|
2020-07-09 15:24:28 +00:00
|
|
|
%Page{total: total_comments, elements: comments} =
|
2020-08-31 15:26:08 +00:00
|
|
|
if actor_id == relay_actor_id do
|
|
|
|
Discussions.list_local_comments(page, limit)
|
|
|
|
else
|
|
|
|
Discussions.list_public_comments_for_actor(actor, page, limit)
|
|
|
|
end
|
2018-12-14 10:23:36 +00:00
|
|
|
|
2019-04-25 17:05:05 +00:00
|
|
|
event_activities = Enum.map(events, &event_to_activity/1)
|
|
|
|
comment_activities = Enum.map(comments, &comment_to_activity/1)
|
|
|
|
activities = event_activities ++ comment_activities
|
2018-08-24 09:34:00 +00:00
|
|
|
|
2019-04-25 17:05:05 +00:00
|
|
|
%{elements: activities, total: total_events + total_comments}
|
2018-05-17 09:32:23 +00:00
|
|
|
end
|
|
|
|
|
2018-11-12 22:30:47 +00:00
|
|
|
# Create an activity from an event
|
2021-11-26 13:30:46 +00:00
|
|
|
@spec event_to_activity(Event.t(), boolean()) :: Activity.t()
|
2018-05-30 12:27:21 +00:00
|
|
|
defp event_to_activity(%Event{} = event, local \\ true) do
|
2018-11-12 17:17:53 +00:00
|
|
|
%Activity{
|
2020-09-02 06:59:59 +00:00
|
|
|
recipients: [@public_ap_adress],
|
2018-05-18 07:56:21 +00:00
|
|
|
actor: event.organizer_actor.url,
|
2020-09-02 06:59:59 +00:00
|
|
|
data: event |> Convertible.model_to_as() |> make_create_data(%{"to" => @public_ap_adress}),
|
2018-11-12 17:17:53 +00:00
|
|
|
local: local
|
2018-05-17 09:32:23 +00:00
|
|
|
}
|
|
|
|
end
|
2018-05-30 12:27:21 +00:00
|
|
|
|
2018-11-12 22:30:47 +00:00
|
|
|
# Create an activity from a comment
|
2021-11-26 13:30:46 +00:00
|
|
|
@spec comment_to_activity(Comment.t(), boolean()) :: Activity.t()
|
2019-02-22 15:11:57 +00:00
|
|
|
defp comment_to_activity(%Comment{} = comment, local \\ true) do
|
2018-11-12 17:17:53 +00:00
|
|
|
%Activity{
|
2020-09-02 06:59:59 +00:00
|
|
|
recipients: [@public_ap_adress],
|
2018-08-24 09:34:00 +00:00
|
|
|
actor: comment.actor.url,
|
2020-09-02 06:59:59 +00:00
|
|
|
data:
|
|
|
|
comment |> Convertible.model_to_as() |> make_create_data(%{"to" => @public_ap_adress}),
|
2018-11-12 17:17:53 +00:00
|
|
|
local: local
|
2018-08-24 09:34:00 +00:00
|
|
|
}
|
|
|
|
end
|
2018-05-17 09:32:23 +00:00
|
|
|
end
|