X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Factivity_pub%2Futils.ex;h=10ce5eee8f0755d4bccc5891cd113b9cbbfddec1;hb=1262357ddb4b889337a931a39b3e28bb3d81f944;hp=01aacbde3fbac90d68f08d12ed0497383d4ceb22;hpb=9374b527569d9182773fe52209cdb86193dbf6a2;p=akkoma diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 01aacbde3..10ce5eee8 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -22,7 +22,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do require Logger require Pleroma.Constants - @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer", "Audio"] + @supported_object_types [ + "Article", + "Note", + "Event", + "Video", + "Page", + "Question", + "Answer", + "Audio" + ] @strip_status_report_states ~w(closed resolved) @supported_report_states ~w(open closed resolved) @valid_visibilities ~w(public unlisted private direct) @@ -299,23 +308,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do def make_emoji_reaction_data(user, object, emoji, activity_id) do make_like_data(user, object, activity_id) - |> Map.put("type", "EmojiReaction") + |> Map.put("type", "EmojiReact") |> Map.put("content", emoji) end - @spec update_element_in_object(String.t(), list(any), Object.t()) :: + @spec update_element_in_object(String.t(), list(any), Object.t(), integer() | nil) :: {:ok, Object.t()} | {:error, Ecto.Changeset.t()} - def update_element_in_object(property, element, object) do + def update_element_in_object(property, element, object, count \\ nil) do length = - if is_map(element) do - element - |> Map.values() - |> List.flatten() - |> length() - else - element - |> length() - end + count || + length(element) data = Map.merge( @@ -335,29 +337,60 @@ defmodule Pleroma.Web.ActivityPub.Utils do %Activity{data: %{"content" => emoji, "actor" => actor}}, object ) do - reactions = object.data["reactions"] || %{} - emoji_actors = reactions[emoji] || [] - new_emoji_actors = [actor | emoji_actors] |> Enum.uniq() - new_reactions = Map.put(reactions, emoji, new_emoji_actors) - update_element_in_object("reaction", new_reactions, object) + reactions = get_cached_emoji_reactions(object) + + new_reactions = + case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do + nil -> + reactions ++ [[emoji, [actor]]] + + index -> + List.update_at( + reactions, + index, + fn [emoji, users] -> [emoji, Enum.uniq([actor | users])] end + ) + end + + count = emoji_count(new_reactions) + + update_element_in_object("reaction", new_reactions, object, count) + end + + def emoji_count(reactions_list) do + Enum.reduce(reactions_list, 0, fn [_, users], acc -> acc + length(users) end) end def remove_emoji_reaction_from_object( %Activity{data: %{"content" => emoji, "actor" => actor}}, object ) do - reactions = object.data["reactions"] || %{} - emoji_actors = reactions[emoji] || [] - new_emoji_actors = List.delete(emoji_actors, actor) + reactions = get_cached_emoji_reactions(object) new_reactions = - if new_emoji_actors == [] do - Map.delete(reactions, emoji) - else - Map.put(reactions, emoji, new_emoji_actors) + case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do + nil -> + reactions + + index -> + List.update_at( + reactions, + index, + fn [emoji, users] -> [emoji, List.delete(users, actor)] end + ) + |> Enum.reject(fn [_, users] -> Enum.empty?(users) end) end - update_element_in_object("reaction", new_reactions, object) + count = emoji_count(new_reactions) + update_element_in_object("reaction", new_reactions, object, count) + end + + def get_cached_emoji_reactions(object) do + if is_list(object.data["reactions"]) do + object.data["reactions"] + else + [] + end end @spec add_like_to_object(Activity.t(), Object.t()) :: @@ -457,10 +490,19 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Repo.one() end + def fetch_latest_undo(%User{ap_id: ap_id}) do + "Undo" + |> Activity.Queries.by_type() + |> where(actor: ^ap_id) + |> order_by([activity], fragment("? desc nulls last", activity.id)) + |> limit(1) + |> Repo.one() + end + def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do %{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id) - "EmojiReaction" + "EmojiReact" |> Activity.Queries.by_type() |> where(actor: ^ap_id) |> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji)) @@ -722,16 +764,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do act when is_binary(act) -> act end - activity = Activity.get_by_ap_id_with_object(id) - actor = User.get_by_ap_id(activity.object.data["actor"]) + case Activity.get_by_ap_id_with_object(id) do + %Activity{} = activity -> + %{ + "type" => "Note", + "id" => activity.data["id"], + "content" => activity.object.data["content"], + "published" => activity.object.data["published"], + "actor" => + AccountView.render("show.json", %{ + user: User.get_by_ap_id(activity.object.data["actor"]) + }) + } - %{ - "type" => "Note", - "id" => activity.data["id"], - "content" => activity.object.data["content"], - "published" => activity.object.data["published"], - "actor" => AccountView.render("show.json", %{user: actor}) - } + _ -> + %{"id" => id, "deleted" => true} + end end defp build_flag_object(_), do: [] @@ -781,6 +829,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do params |> Map.put("type", "Flag") |> Map.put("skip_preload", true) + |> Map.put("preload_report_notes", true) |> Map.put("total", true) |> Map.put("limit", page_size) |> Map.put("offset", (page - 1) * page_size) @@ -788,63 +837,76 @@ defmodule Pleroma.Web.ActivityPub.Utils do ActivityPub.fetch_activities([], params, :offset) end - @spec get_reports_grouped_by_status(%{required(:activity) => String.t()}) :: %{ - required(:groups) => [ - %{ - required(:date) => String.t(), - required(:account) => %{}, - required(:status) => %{}, - required(:actors) => [%User{}], - required(:reports) => [%Activity{}] - } - ], - required(:total) => integer - } - def get_reports_grouped_by_status(groups) do - parsed_groups = - groups - |> Enum.map(fn entry -> - activity = - case Jason.decode(entry.activity) do - {:ok, activity} -> activity - _ -> build_flag_object(entry.activity) - end - - parse_report_group(activity) - end) - - %{ - groups: parsed_groups - } - end - def parse_report_group(activity) do reports = get_reports_by_status_id(activity["id"]) max_date = Enum.max_by(reports, &NaiveDateTime.from_iso8601!(&1.data["published"])) actors = Enum.map(reports, & &1.user_actor) + [%{data: %{"object" => [account_id | _]}} | _] = reports + + account = + AccountView.render("show.json", %{ + user: User.get_by_ap_id(account_id) + }) + + status = get_status_data(activity) %{ date: max_date.data["published"], - account: activity["actor"], - status: %{ - id: activity["id"], - content: activity["content"], - published: activity["published"] - }, + account: account, + status: status, actors: Enum.uniq(actors), reports: reports } end + defp get_status_data(status) do + case status["deleted"] do + true -> + %{ + "id" => status["id"], + "deleted" => true + } + + _ -> + Activity.get_by_ap_id(status["id"]) + end + end + def get_reports_by_status_id(ap_id) do from(a in Activity, where: fragment("(?)->>'type' = 'Flag'", a.data), - where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}]) + where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}]), + or_where: fragment("(?)->'object' @> ?", a.data, ^[ap_id]) ) |> Activity.with_preloaded_user_actor() |> Repo.all() end + @spec get_reports_grouped_by_status([String.t()]) :: %{ + required(:groups) => [ + %{ + required(:date) => String.t(), + required(:account) => %{}, + required(:status) => %{}, + required(:actors) => [%User{}], + required(:reports) => [%Activity{}] + } + ] + } + def get_reports_grouped_by_status(activity_ids) do + parsed_groups = + activity_ids + |> Enum.map(fn id -> + id + |> build_flag_object() + |> parse_report_group() + end) + + %{ + groups: parsed_groups + } + end + @spec get_reported_activities() :: [ %{ required(:activity) => String.t(), @@ -852,17 +914,23 @@ defmodule Pleroma.Web.ActivityPub.Utils do } ] def get_reported_activities do - from(a in Activity, - where: fragment("(?)->>'type' = 'Flag'", a.data), + reported_activities_query = + from(a in Activity, + where: fragment("(?)->>'type' = 'Flag'", a.data), + select: %{ + activity: fragment("jsonb_array_elements((? #- '{object,0}')->'object')", a.data) + }, + group_by: fragment("activity") + ) + + from(a in subquery(reported_activities_query), + distinct: true, select: %{ - date: fragment("max(?->>'published') date", a.data), - activity: - fragment("jsonb_array_elements_text((? #- '{object,0}')->'object') activity", a.data) - }, - group_by: fragment("activity"), - order_by: fragment("date DESC") + id: fragment("COALESCE(?->>'id'::text, ? #>> '{}')", a.activity, a.activity) + } ) |> Repo.all() + |> Enum.map(& &1.id) end def update_report_state(%Activity{} = activity, state)