Fetch user's outbox posts on first federation with that user
authorEkaterina Vaartis <vaartis@cock.li>
Wed, 6 Mar 2019 21:13:26 +0000 (00:13 +0300)
committerEkaterina Vaartis <vaartis@cock.li>
Fri, 8 Mar 2019 13:27:56 +0000 (16:27 +0300)
config/config.exs
docs/config.md
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/utils.ex

index a867dd0bcbe1aa6bc51eed5be27383018fa4b6d9..2b9aabf80c9c230612ecb7fd4ebec2b5c09d43c5 100644 (file)
@@ -348,6 +348,10 @@ config :pleroma, Pleroma.Jobs,
   federator_outgoing: [max_jobs: 50],
   mailer: [max_jobs: 10]
 
+config :pleroma, :fetch_initial_posts,
+  enabled: false,
+  pages: 5
+
 config :auto_linker,
   opts: [
     scheme: true,
index 465bc1d2b9b26cc0a81b1197afd96cec04a8d296..a09ea95f3def569fffa666e27f30c95e84b4e98d 100644 (file)
@@ -285,6 +285,10 @@ This config contains two queues: `federator_incoming` and `federator_outgoing`.
 ## :rich_media
 * `enabled`: if enabled the instance will parse metadata from attached links to generate link previews
 
+## :fetch_initial_posts
+* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
+* `pages`: the amount of pages to fetch
+
 ## :hackney_pools
 
 Advanced. Tweaks Hackney (http client) connections pools.
index 50e7e7ccd64f8a53877dcaf2cc25ae3be1463d4e..01063c813f2be83be03435c8b1336a323609f212 100644 (file)
@@ -532,6 +532,10 @@ defmodule Pleroma.User do
       _e ->
         with [_nick, _domain] <- String.split(nickname, "@"),
              {:ok, user} <- fetch_by_nickname(nickname) do
+          if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
+            {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
+          end
+
           user
         else
           _e -> nil
@@ -539,6 +543,17 @@ defmodule Pleroma.User do
     end
   end
 
+  @doc "Fetch some posts when the user has just been federated with"
+  def fetch_initial_posts(user) do
+    pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
+
+    Enum.each(
+      # Insert all the posts in reverse order, so they're in the right order on the timeline
+      Enum.reverse(Utils.fetch_ordered_collection(user.info.source_data["outbox"], pages)),
+      &Pleroma.Web.Federator.incoming_ap_doc/1
+    )
+  end
+
   def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do
     from(
       u in User,
@@ -1108,24 +1123,36 @@ defmodule Pleroma.User do
 
   def html_filter_policy(_), do: @default_scrubbers
 
+  def fetch_by_ap_id(ap_id) do
+    ap_try = ActivityPub.make_user_from_ap_id(ap_id)
+
+    case ap_try do
+      {:ok, user} ->
+        user
+
+      _ ->
+        case OStatus.make_user(ap_id) do
+          {:ok, user} -> user
+          _ -> {:error, "Could not fetch by AP id"}
+        end
+    end
+  end
+
   def get_or_fetch_by_ap_id(ap_id) do
     user = get_by_ap_id(ap_id)
 
     if !is_nil(user) and !User.needs_update?(user) do
       user
     else
-      ap_try = ActivityPub.make_user_from_ap_id(ap_id)
-
-      case ap_try do
-        {:ok, user} ->
-          user
+      user = fetch_by_ap_id(ap_id)
 
-        _ ->
-          case OStatus.make_user(ap_id) do
-            {:ok, user} -> user
-            _ -> {:error, "Could not fetch by AP id"}
-          end
+      if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
+        with %User{} = user do
+          {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
+        end
       end
+
+      user
     end
   end
 
index 9e50789db8b7a595ffc26ba257e9c088e5fd9385..629c39315ec5095690bf634f224137aeeb50555f 100644 (file)
@@ -633,4 +633,43 @@ defmodule Pleroma.Web.ActivityPub.Utils do
     }
     |> Map.merge(additional)
   end
+
+  @doc """
+  Fetches the OrderedCollection/OrderedCollectionPage from `from`, limiting the amount of pages fetched after
+  the first one to `pages_left` pages.
+  If the amount of pages is higher than the collection has, it returns whatever was there.
+  """
+  def fetch_ordered_collection(from, pages_left, acc \\ []) do
+    with {:ok, response} <- Tesla.get(from),
+         {:ok, collection} <- Poison.decode(response.body) do
+      case collection["type"] do
+        "OrderedCollection" ->
+          # If we've encountered the OrderedCollection and not the page,
+          # just call the same function on the page address
+          fetch_ordered_collection(collection["first"], pages_left)
+
+        "OrderedCollectionPage" ->
+          if pages_left > 0 do
+            # There are still more pages
+            if Map.has_key?(collection, "next") do
+              # There are still more pages, go deeper saving what we have into the accumulator
+              fetch_ordered_collection(
+                collection["next"],
+                pages_left - 1,
+                acc ++ collection["orderedItems"]
+              )
+            else
+              # No more pages left, just return whatever we already have
+              acc ++ collection["orderedItems"]
+            end
+          else
+            # Got the amount of pages needed, add them all to the accumulator
+            acc ++ collection["orderedItems"]
+          end
+
+        _ ->
+          {:error, "Not an OrderedCollection or OrderedCollectionPage"}
+      end
+    end
+  end
 end