Add account export
authorEgor Kislitsyn <egor@kislitsyn.com>
Wed, 19 Aug 2020 11:31:33 +0000 (15:31 +0400)
committerEgor Kislitsyn <egor@kislitsyn.com>
Wed, 7 Oct 2020 14:34:27 +0000 (18:34 +0400)
lib/pleroma/export.ex [new file with mode: 0644]

diff --git a/lib/pleroma/export.ex b/lib/pleroma/export.ex
new file mode 100644 (file)
index 0000000..82a4b7a
--- /dev/null
@@ -0,0 +1,118 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Export do
+  alias Pleroma.Activity
+  alias Pleroma.Bookmark
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Transmogrifier
+  alias Pleroma.Web.ActivityPub.UserView
+
+  import Ecto.Query
+
+  def run(user) do
+    with {:ok, dir} <- create_dir(),
+         :ok <- actor(dir, user),
+         :ok <- statuses(dir, user),
+         :ok <- likes(dir, user),
+         :ok <- bookmarks(dir, user) do
+      IO.inspect({"DONE", dir})
+    else
+      err -> IO.inspect({"export error", err})
+    end
+  end
+
+  def actor(dir, user) do
+    with {:ok, json} <-
+           UserView.render("user.json", %{user: user})
+           |> Map.merge(%{"likes" => "likes.json", "bookmarks" => "bookmarks.json"})
+           |> Jason.encode() do
+      File.write(dir <> "/actor.json", json)
+    end
+  end
+
+  defp create_dir do
+    datetime = Calendar.NaiveDateTime.Format.iso8601_basic(NaiveDateTime.utc_now())
+    dir = Path.join(System.tmp_dir!(), "archive-" <> datetime)
+
+    with :ok <- File.mkdir(dir), do: {:ok, dir}
+  end
+
+  defp write_header(file, name) do
+    IO.write(
+      file,
+      """
+      {
+        "@context": "https://www.w3.org/ns/activitystreams",
+        "id": "#{name}.json",
+        "type": "OrderedCollection",
+        "orderedItems": [
+      """
+    )
+  end
+
+  defp write(query, dir, name, fun) do
+    path = dir <> "/#{name}.json"
+
+    with {:ok, file} <- File.open(path, [:write, :utf8]),
+         :ok <- write_header(file, name) do
+      counter = :counters.new(1, [])
+
+      query
+      |> Pleroma.RepoStreamer.chunk_stream(100)
+      |> Stream.each(fn items ->
+        Enum.each(items, fn i ->
+          with {:ok, str} <- fun.(i),
+               :ok <- IO.write(file, str <> ",\n") do
+            :counters.add(counter, 1, 1)
+          end
+        end)
+      end)
+      |> Stream.run()
+
+      total = :counters.get(counter, 1)
+
+      with :ok <- :file.pwrite(file, {:eof, -2}, "\n],\n  \"totalItems\": #{total}}") do
+        File.close(file)
+      end
+    end
+  end
+
+  def bookmarks(dir, %{id: user_id} = _user) do
+    Bookmark
+    |> where(user_id: ^user_id)
+    |> join(:inner, [b], activity in assoc(b, :activity))
+    |> select([b, a], %{id: b.id, object: fragment("(?)->>'object'", a.data)})
+    |> write(dir, "bookmarks", fn a -> {:ok, "\"#{a.object}\""} end)
+  end
+
+  def likes(dir, user) do
+    user.ap_id
+    |> Activity.Queries.by_actor()
+    |> Activity.Queries.by_type("Like")
+    |> select([like], %{id: like.id, object: fragment("(?)->>'object'", like.data)})
+    |> write(dir, "likes", fn a -> {:ok, "\"#{a.object}\""} end)
+  end
+
+  def statuses(dir, user) do
+    opts =
+      %{}
+      |> Map.put(:type, ["Create", "Announce"])
+      |> Map.put(:blocking_user, user)
+      |> Map.put(:muting_user, user)
+      |> Map.put(:reply_filtering_user, user)
+      |> Map.put(:announce_filtering_user, user)
+      |> Map.put(:user, user)
+
+    [[user.ap_id], User.following(user), Pleroma.List.memberships(user)]
+    |> Enum.concat()
+    |> ActivityPub.fetch_activities_query(opts)
+    |> write(dir, "outbox", fn a ->
+      with {:ok, activity} <- Transmogrifier.prepare_outgoing(a.data) do
+        activity |> Map.delete("@context") |> Jason.encode()
+      end
+    end)
+  end
+end