Add ActivityPub Object Event type support
[akkoma] / lib / pleroma / web / activity_pub / utils.ex
index 91cfa21943fd876b503c5b7d7af0bcfd0718cf31..db70842461c29f5376b56db5337affc2d54c24e8 100644 (file)
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web
+  alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.AdminAPI.AccountView
   alias Pleroma.Web.Endpoint
@@ -21,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)
@@ -706,26 +716,37 @@ defmodule Pleroma.Web.ActivityPub.Utils do
   def make_flag_data(_, _), do: %{}
 
   defp build_flag_object(%{account: account, statuses: statuses} = _) do
-    [account.ap_id] ++
-      Enum.map(statuses || [], fn act ->
-        id =
-          case act do
-            %Activity{} = act -> act.data["id"]
-            act when is_map(act) -> act["id"]
-            act when is_binary(act) -> act
-          end
+    [account.ap_id] ++ build_flag_object(%{statuses: statuses})
+  end
 
-        activity = Activity.get_by_ap_id_with_object(id)
-        actor = User.get_by_ap_id(activity.object.data["actor"])
+  defp build_flag_object(%{statuses: statuses}) do
+    Enum.map(statuses || [], &build_flag_object/1)
+  end
 
+  defp build_flag_object(act) when is_map(act) or is_binary(act) do
+    id =
+      case act do
+        %Activity{} = act -> act.data["id"]
+        act when is_map(act) -> act["id"]
+        act when is_binary(act) -> act
+      end
+
+    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: actor})
+          "actor" =>
+            AccountView.render("show.json", %{
+              user: User.get_by_ap_id(activity.object.data["actor"])
+            })
         }
-      end)
+
+      _ ->
+        %{"id" => id, "deleted" => true}
+    end
   end
 
   defp build_flag_object(_), do: []
@@ -770,6 +791,114 @@ defmodule Pleroma.Web.ActivityPub.Utils do
   end
 
   #### Report-related helpers
+  def get_reports(params, page, page_size) do
+    params =
+      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)
+
+    ActivityPub.fetch_activities([], params, :offset)
+  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: 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}]),
+      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(),
+            required(:date) => String.t()
+          }
+        ]
+  def get_reported_activities do
+    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: %{
+        id: fragment("COALESCE(?->>'id'::text, ? #>> '{}')", a.activity, a.activity)
+      }
+    )
+    |> Repo.all()
+    |> Enum.map(& &1.id)
+  end
 
   def update_report_state(%Activity{} = activity, state)
       when state in @strip_status_report_states do
@@ -793,11 +922,29 @@ defmodule Pleroma.Web.ActivityPub.Utils do
     |> Repo.update()
   end
 
+  def update_report_state(activity_ids, state) when state in @supported_report_states do
+    activities_num = length(activity_ids)
+
+    from(a in Activity, where: a.id in ^activity_ids)
+    |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
+    |> Repo.update_all([])
+    |> case do
+      {^activities_num, _} -> :ok
+      _ -> {:error, activity_ids}
+    end
+  end
+
   def update_report_state(_, _), do: {:error, "Unsupported state"}
 
   def strip_report_status_data(activity) do
     [actor | reported_activities] = activity.data["object"]
-    stripped_activities = Enum.map(reported_activities, & &1["id"])
+
+    stripped_activities =
+      Enum.map(reported_activities, fn
+        act when is_map(act) -> act["id"]
+        act when is_binary(act) -> act
+      end)
+
     new_data = put_in(activity.data, ["object"], [actor | stripped_activities])
 
     {:ok, %{activity | data: new_data}}