Refactor Mobilizon.Federation.ActivityPub.Audience and add tests
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -426,7 +426,7 @@ defmodule Mobilizon.Federation.ActivityPub do
|
||||
"id" => "#{Endpoint.url()}/leave/event/#{participant.id}"
|
||||
},
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(participant),
|
||||
Audience.get_audience(participant),
|
||||
{:ok, activity} <- create_activity(Map.merge(leave_data, audience), local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, participant}
|
||||
@@ -803,7 +803,7 @@ defmodule Mobilizon.Federation.ActivityPub do
|
||||
Scheduler.trigger_notifications_for_participant(participant),
|
||||
participant_as_data <- Convertible.model_to_as(participant),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(participant),
|
||||
Audience.get_audience(participant),
|
||||
update_data <-
|
||||
make_accept_join_data(
|
||||
participant_as_data,
|
||||
@@ -837,7 +837,7 @@ defmodule Mobilizon.Federation.ActivityPub do
|
||||
),
|
||||
member_as_data <- Convertible.model_to_as(member),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(member),
|
||||
Audience.get_audience(member),
|
||||
update_data <-
|
||||
make_accept_join_data(
|
||||
member_as_data,
|
||||
@@ -899,7 +899,7 @@ defmodule Mobilizon.Federation.ActivityPub do
|
||||
participant_as_data <- Convertible.model_to_as(participant),
|
||||
audience <-
|
||||
participant
|
||||
|> Audience.calculate_to_and_cc_from_mentions()
|
||||
|> Audience.get_audience()
|
||||
|> Map.merge(additional),
|
||||
reject_data <- %{
|
||||
"type" => "Reject",
|
||||
@@ -925,7 +925,7 @@ defmodule Mobilizon.Federation.ActivityPub do
|
||||
with {:ok, %Follower{} = follower} <- Actors.delete_follower(follower),
|
||||
follower_as_data <- Convertible.model_to_as(follower),
|
||||
audience <-
|
||||
follower.actor |> Audience.calculate_to_and_cc_from_mentions() |> Map.merge(additional),
|
||||
follower.actor |> Audience.get_audience() |> Map.merge(additional),
|
||||
reject_data <- %{
|
||||
"to" => [follower.actor.url],
|
||||
"type" => "Reject",
|
||||
|
||||
@@ -3,18 +3,95 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
Tools for calculating content audience
|
||||
"""
|
||||
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.{Actors, Events, Share}
|
||||
alias Mobilizon.Actors.{Actor, Member}
|
||||
alias Mobilizon.Discussions.{Comment, Discussion}
|
||||
alias Mobilizon.Events.{Event, Participant}
|
||||
alias Mobilizon.Posts.Post
|
||||
alias Mobilizon.Share
|
||||
alias Mobilizon.Storage.Repo
|
||||
|
||||
require Logger
|
||||
|
||||
@ap_public "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
@type audience :: %{required(String.t()) => list(String.t())}
|
||||
|
||||
@doc """
|
||||
Get audience for an entity
|
||||
"""
|
||||
@spec get_audience(Entity.t()) :: audience()
|
||||
def get_audience(%Event{} = event) do
|
||||
extract_actors_from_event(event)
|
||||
end
|
||||
|
||||
def get_audience(%Post{draft: true} = post) do
|
||||
get_audience(%Post{post | visibility: :private, draft: false})
|
||||
end
|
||||
|
||||
def get_audience(%Post{attributed_to: %Actor{} = group, visibility: visibility}) do
|
||||
{to, cc} = get_to_and_cc(group, [], visibility)
|
||||
%{"to" => to, "cc" => cc}
|
||||
end
|
||||
|
||||
def get_audience(%Discussion{actor: actor}) do
|
||||
%{"to" => maybe_add_group_members([], actor), "cc" => []}
|
||||
end
|
||||
|
||||
def get_audience(%Comment{discussion: %Discussion{} = discussion}) do
|
||||
get_audience(discussion)
|
||||
end
|
||||
|
||||
def get_audience(%Comment{
|
||||
mentions: mentions,
|
||||
actor: %Actor{} = actor,
|
||||
visibility: visibility,
|
||||
in_reply_to_comment: in_reply_to_comment,
|
||||
event: event,
|
||||
origin_comment: origin_comment,
|
||||
url: url
|
||||
}) do
|
||||
with {to, cc} <-
|
||||
extract_actors_from_mentions(mentions, actor, visibility),
|
||||
{to, cc} <- {Enum.uniq(to ++ add_in_reply_to(in_reply_to_comment)), cc},
|
||||
{to, cc} <- {Enum.uniq(to ++ add_event_author(event)), cc},
|
||||
{to, cc} <-
|
||||
{to,
|
||||
Enum.uniq(
|
||||
cc ++
|
||||
add_comments_authors([origin_comment]) ++
|
||||
add_shares_actors_followers(url)
|
||||
)} do
|
||||
%{"to" => to, "cc" => cc}
|
||||
end
|
||||
end
|
||||
|
||||
def get_audience(%Participant{} = participant) do
|
||||
event = Events.get_event_with_preload!(participant.event_id)
|
||||
|
||||
actor_participants_urls =
|
||||
event.id
|
||||
|> Mobilizon.Events.list_actors_participants_for_event()
|
||||
|> Enum.map(& &1.url)
|
||||
|
||||
%{
|
||||
"to" => [participant.actor.url, group_or_organizer_event(event).url],
|
||||
"cc" => actor_participants_urls
|
||||
}
|
||||
end
|
||||
|
||||
def get_audience(%Member{} = member) do
|
||||
%{"to" => [member.parent.url, member.parent.members_url], "cc" => []}
|
||||
end
|
||||
|
||||
def get_audience(%Actor{} = actor) do
|
||||
%{
|
||||
"to" => [@ap_public],
|
||||
"cc" =>
|
||||
maybe_add_group_members([actor.followers_url], actor) ++
|
||||
add_actors_that_had_our_content(actor.id)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Determines the full audience based on mentions for an audience
|
||||
|
||||
@@ -39,6 +116,8 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
to = [@ap_public | mentions]
|
||||
cc = [actor.followers_url]
|
||||
|
||||
cc = maybe_add_group_members(cc, actor)
|
||||
|
||||
{to, cc}
|
||||
end
|
||||
|
||||
@@ -47,13 +126,18 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
to = [actor.followers_url | mentions]
|
||||
cc = [@ap_public]
|
||||
|
||||
to = maybe_add_group_members(to, actor)
|
||||
|
||||
{to, cc}
|
||||
end
|
||||
|
||||
@spec get_to_and_cc(Actor.t(), list(), String.t()) :: {list(), list()}
|
||||
def get_to_and_cc(%Actor{} = actor, mentions, :private) do
|
||||
{to, cc} = get_to_and_cc(actor, mentions, :direct)
|
||||
{[actor.followers_url | to], cc}
|
||||
|
||||
to = maybe_add_group_members(to, actor)
|
||||
|
||||
{to, cc}
|
||||
end
|
||||
|
||||
@spec get_to_and_cc(Actor.t(), list(), String.t()) :: {list(), list()}
|
||||
@@ -65,125 +149,24 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
{mentions, []}
|
||||
end
|
||||
|
||||
@spec maybe_add_group_members(List.t(), Actor.t()) :: List.t()
|
||||
defp maybe_add_group_members(collection, %Actor{type: :Group, members_url: members_url}) do
|
||||
[members_url | collection]
|
||||
end
|
||||
|
||||
defp maybe_add_group_members(collection, %Actor{type: _}), do: collection
|
||||
|
||||
def get_addressed_actors(mentioned_users, _), do: mentioned_users
|
||||
|
||||
def calculate_to_and_cc_from_mentions(
|
||||
%Comment{discussion: %Discussion{actor_id: actor_id}} = _comment
|
||||
) do
|
||||
with %Actor{type: :Group, members_url: members_url} <- Actors.get_actor(actor_id) do
|
||||
%{"to" => [members_url], "cc" => []}
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(%Comment{} = comment) do
|
||||
with {to, cc} <-
|
||||
extract_actors_from_mentions(comment.mentions, comment.actor, comment.visibility),
|
||||
{to, cc} <- {Enum.uniq(to ++ add_in_reply_to(comment.in_reply_to_comment)), cc},
|
||||
{to, cc} <- {Enum.uniq(to ++ add_event_author(comment.event)), cc},
|
||||
{to, cc} <-
|
||||
{to,
|
||||
Enum.uniq(
|
||||
cc ++
|
||||
add_comments_authors([comment.origin_comment]) ++
|
||||
add_shares_actors_followers(comment.url)
|
||||
)} do
|
||||
%{"to" => to, "cc" => cc}
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(%Discussion{actor_id: actor_id}) do
|
||||
with %Actor{type: :Group, members_url: members_url} <- Actors.get_actor(actor_id) do
|
||||
%{"to" => [members_url], "cc" => []}
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(
|
||||
%Event{
|
||||
attributed_to: %Actor{members_url: members_url},
|
||||
visibility: visibility
|
||||
} = event
|
||||
) do
|
||||
%{"to" => to, "cc" => cc} = extract_actors_from_event(event)
|
||||
|
||||
case visibility do
|
||||
:public ->
|
||||
%{"to" => [@ap_public, members_url] ++ to, "cc" => [] ++ cc}
|
||||
|
||||
:unlisted ->
|
||||
%{"to" => [members_url] ++ to, "cc" => [@ap_public] ++ cc}
|
||||
|
||||
:private ->
|
||||
# Private is restricted to only the members
|
||||
%{"to" => [members_url], "cc" => []}
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(%Event{} = event) do
|
||||
extract_actors_from_event(event)
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(%Post{
|
||||
attributed_to: %Actor{members_url: members_url, followers_url: followers_url},
|
||||
visibility: visibility,
|
||||
draft: draft
|
||||
}) do
|
||||
cond do
|
||||
# If the post is draft we send it only to members
|
||||
draft == true ->
|
||||
%{"to" => [members_url], "cc" => []}
|
||||
|
||||
# If public everyone
|
||||
visibility == :public ->
|
||||
%{"to" => [@ap_public, members_url], "cc" => [followers_url]}
|
||||
|
||||
# Otherwise just followers
|
||||
visibility == :unlisted ->
|
||||
%{"to" => [followers_url, members_url], "cc" => [@ap_public]}
|
||||
|
||||
visibility == :private ->
|
||||
# Private is restricted to only the members
|
||||
%{"to" => [members_url], "cc" => []}
|
||||
|
||||
true ->
|
||||
%{"to" => [], "cc" => []}
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(%Participant{} = participant) do
|
||||
participant = Repo.preload(participant, [:actor, :event])
|
||||
|
||||
actor_participants_urls =
|
||||
participant.event.id
|
||||
|> Mobilizon.Events.list_actors_participants_for_event()
|
||||
|> Enum.map(& &1.url)
|
||||
|
||||
%{"to" => [participant.actor.url], "cc" => actor_participants_urls}
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(%Member{} = member) do
|
||||
member = Repo.preload(member, [:parent])
|
||||
|
||||
%{"to" => [member.parent.members_url], "cc" => []}
|
||||
end
|
||||
|
||||
def calculate_to_and_cc_from_mentions(%Actor{} = actor) do
|
||||
%{
|
||||
"to" => [@ap_public],
|
||||
"cc" => [actor.followers_url] ++ add_actors_that_had_our_content(actor.id)
|
||||
}
|
||||
end
|
||||
|
||||
defp add_in_reply_to(%Comment{actor: %Actor{url: url}} = _comment), do: [url]
|
||||
defp add_in_reply_to(%Event{organizer_actor: %Actor{url: url}} = _event), do: [url]
|
||||
defp add_in_reply_to(_), do: []
|
||||
|
||||
defp add_event_author(nil), do: []
|
||||
|
||||
defp add_event_author(%Event{} = event) do
|
||||
[Repo.preload(event, [:organizer_actor]).organizer_actor.url]
|
||||
end
|
||||
|
||||
defp add_comment_author(nil), do: nil
|
||||
defp add_event_author(_), do: []
|
||||
|
||||
defp add_comment_author(%Comment{} = comment) do
|
||||
case Repo.preload(comment, [:actor]) do
|
||||
@@ -195,6 +178,8 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
end
|
||||
end
|
||||
|
||||
defp add_comment_author(_), do: nil
|
||||
|
||||
defp add_comments_authors(comments) do
|
||||
authors =
|
||||
comments
|
||||
@@ -208,8 +193,6 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
defp add_shares_actors_followers(uri) do
|
||||
uri
|
||||
|> Share.get_actors_by_share_uri()
|
||||
|> Enum.map(&Actors.list_followers_actors_for_actor/1)
|
||||
|> List.flatten()
|
||||
|> Enum.map(& &1.url)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
@@ -217,8 +200,6 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
defp add_actors_that_had_our_content(actor_id) do
|
||||
actor_id
|
||||
|> Share.get_actors_by_owner_actor_id()
|
||||
|> Enum.map(&Actors.list_followers_actors_for_actor/1)
|
||||
|> List.flatten()
|
||||
|> Enum.map(& &1.url)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
@@ -241,7 +222,11 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
|
||||
defp extract_actors_from_event(%Event{} = event) do
|
||||
with {to, cc} <-
|
||||
extract_actors_from_mentions(event.mentions, event.organizer_actor, event.visibility),
|
||||
extract_actors_from_mentions(
|
||||
event.mentions,
|
||||
group_or_organizer_event(event),
|
||||
event.visibility
|
||||
),
|
||||
{to, cc} <-
|
||||
{to,
|
||||
Enum.uniq(
|
||||
@@ -253,4 +238,8 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
|
||||
%{"to" => [], "cc" => []}
|
||||
end
|
||||
end
|
||||
|
||||
@spec group_or_organizer_event(Event.t()) :: Actor.t()
|
||||
defp group_or_organizer_event(%Event{attributed_to: %Actor{} = group}), do: group
|
||||
defp group_or_organizer_event(%Event{organizer_actor: %Actor{} = actor}), do: actor
|
||||
end
|
||||
|
||||
@@ -47,7 +47,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
|
||||
actor_as_data <- Convertible.model_to_as(new_actor),
|
||||
{:ok, true} <- Cachex.del(:activity_pub, "actor_#{new_actor.preferred_username}"),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(new_actor),
|
||||
Audience.get_audience(new_actor),
|
||||
additional <- Map.merge(additional, %{"actor" => old_actor.url}),
|
||||
update_data <- make_update_data(actor_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, new_actor, update_data}
|
||||
@@ -142,7 +142,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
|
||||
"object" => group.url
|
||||
},
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(member) do
|
||||
Audience.get_audience(member) do
|
||||
approve_if_default_role_is_member(
|
||||
group,
|
||||
actor,
|
||||
|
||||
@@ -32,7 +32,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
|
||||
:ok <- maybe_publish_graphql_subscription(discussion_id),
|
||||
comment_as_data <- Convertible.model_to_as(comment),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(comment),
|
||||
Audience.get_audience(comment),
|
||||
create_data <-
|
||||
make_create_data(comment_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, comment, create_data}
|
||||
@@ -47,7 +47,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
|
||||
{:ok, true} <- Cachex.del(:activity_pub, "comment_#{new_comment.uuid}"),
|
||||
comment_as_data <- Convertible.model_to_as(new_comment),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(new_comment),
|
||||
Audience.get_audience(new_comment),
|
||||
update_data <- make_update_data(comment_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, new_comment, update_data}
|
||||
else
|
||||
@@ -79,7 +79,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
|
||||
force_deletion = Map.get(options, :force, false)
|
||||
|
||||
with audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(comment),
|
||||
Audience.get_audience(comment),
|
||||
{:ok, %Comment{} = updated_comment} <-
|
||||
Discussions.delete_comment(comment, force: force_deletion),
|
||||
{:ok, true} <- Cachex.del(:activity_pub, "comment_#{comment.uuid}"),
|
||||
|
||||
@@ -31,7 +31,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
|
||||
:ok <- maybe_publish_graphql_subscription(discussion),
|
||||
comment_as_data <- Convertible.model_to_as(last_comment),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(discussion),
|
||||
Audience.get_audience(discussion),
|
||||
create_data <-
|
||||
make_create_data(comment_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, discussion, create_data}
|
||||
@@ -48,7 +48,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
|
||||
DiscussionActivity.insert_activity(discussion, subject: "discussion_created"),
|
||||
discussion_as_data <- Convertible.model_to_as(discussion),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(discussion),
|
||||
Audience.get_audience(discussion),
|
||||
create_data <-
|
||||
make_create_data(discussion_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, discussion, create_data}
|
||||
@@ -68,7 +68,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
|
||||
{:ok, true} <- Cachex.del(:activity_pub, "discussion_#{new_discussion.slug}"),
|
||||
discussion_as_data <- Convertible.model_to_as(new_discussion),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(new_discussion),
|
||||
Audience.get_audience(new_discussion),
|
||||
update_data <- make_update_data(discussion_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, new_discussion, update_data}
|
||||
else
|
||||
|
||||
@@ -29,7 +29,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
|
||||
EventActivity.insert_activity(event, subject: "event_created"),
|
||||
event_as_data <- Convertible.model_to_as(event),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(event),
|
||||
Audience.get_audience(event),
|
||||
create_data <-
|
||||
make_create_data(event_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, event, create_data}
|
||||
@@ -46,7 +46,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
|
||||
{:ok, true} <- Cachex.del(:activity_pub, "event_#{new_event.uuid}"),
|
||||
event_as_data <- Convertible.model_to_as(new_event),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(new_event),
|
||||
Audience.get_audience(new_event),
|
||||
update_data <- make_update_data(event_as_data, Map.merge(audience, additional)) do
|
||||
{:ok, new_event, update_data}
|
||||
else
|
||||
@@ -69,7 +69,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
|
||||
}
|
||||
|
||||
with audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(event),
|
||||
Audience.get_audience(event),
|
||||
{:ok, %Event{} = event} <- EventsManager.delete_event(event),
|
||||
{:ok, _} <-
|
||||
EventActivity.insert_activity(event, subject: "event_deleted"),
|
||||
@@ -124,7 +124,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
|
||||
}),
|
||||
join_data <- Convertible.model_to_as(participant),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(participant) do
|
||||
Audience.get_audience(participant) do
|
||||
approve_if_default_role_is_participant(
|
||||
event,
|
||||
Map.merge(join_data, audience),
|
||||
|
||||
@@ -47,7 +47,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Posts do
|
||||
post_as_data <-
|
||||
Convertible.model_to_as(%{post | attributed_to: group, author: creator}),
|
||||
audience <-
|
||||
Audience.calculate_to_and_cc_from_mentions(post) do
|
||||
Audience.get_audience(post) do
|
||||
update_data = make_update_data(post_as_data, Map.merge(audience, additional))
|
||||
|
||||
{:ok, post, update_data}
|
||||
|
||||
@@ -41,7 +41,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
|
||||
}
|
||||
} = post
|
||||
) do
|
||||
audience = Audience.calculate_to_and_cc_from_mentions(post)
|
||||
audience = Audience.get_audience(post)
|
||||
|
||||
%{
|
||||
"type" => "Article",
|
||||
|
||||
Reference in New Issue
Block a user