From 26eb11c172e2de2db0f4c51a9df5f0369fb37a95 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 26 Oct 2018 06:16:51 +0000 Subject: [PATCH 1/3] activitypub: add support for filtering broken threads out of timelines --- lib/pleroma/web/activity_pub/activity_pub.ex | 33 +++++++++++++++++++ .../mastodon_api/mastodon_api_controller.ex | 1 + .../web/twitter_api/twitter_api_controller.ex | 4 ++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index fc191addf..173ca688d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -787,4 +787,37 @@ def visible_for_user?(activity, user) do y = activity.data["to"] ++ (activity.data["cc"] || []) visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y)) end + + # guard + def entire_thread_visible_for_user?(nil, user), do: false + + # child + def entire_thread_visible_for_user?( + %Activity{data: %{"object" => %{"inReplyTo" => _parent_id}}} = tail, + user + ) do + parent = Activity.get_in_reply_to_activity(tail) + visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user) + end + + # root + def entire_thread_visible_for_user?(tail, user), do: visible_for_user?(tail, user) + + # filter out broken threads + def contain_broken_threads(%Activity{} = activity, %User{} = user) do + entire_thread_visible_for_user?(activity, user) + end + + # do post-processing on a specific activity + def contain_activity(%Activity{} = activity, %User{} = user) do + contain_broken_threads(activity, user) + end + + # do post-processing on a timeline + def contain_timeline(timeline, user) do + timeline + |> Enum.filter(fn activity -> + contain_activity(activity, user) + end) + end end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 751698ca8..bc7558cb8 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -223,6 +223,7 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) + |> ActivityPub.contain_timeline(user) |> Enum.reverse() conn diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index c6637e38d..4fc32b50c 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -79,7 +79,9 @@ def friends_timeline(%{assigns: %{user: user}} = conn, params) do |> Map.put("blocking_user", user) |> Map.put("user", user) - activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) + activities = + ActivityPub.fetch_activities([user.ap_id | user.following], params) + |> ActivityPub.contain_timeline(user) conn |> render(ActivityView, "index.json", %{activities: activities, for: user}) From 0d0c1dcf440707535afa852503e9afa049cd8374 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 26 Oct 2018 06:35:06 +0000 Subject: [PATCH 2/3] tests: add testsuite for broken thread filter --- test/web/activity_pub/activity_pub_test.exs | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 1cf7d6bbc..14011a010 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -476,6 +476,42 @@ test "it creates a delete activity and deletes the original object" do end end + describe "timeline post-processing" do + test "it filters broken threads" do + user1 = insert(:user) + user2 = insert(:user) + user3 = insert(:user) + + {:ok, user1} = User.follow(user1, user3) + assert User.following?(user1, user3) + + {:ok, user2} = User.follow(user2, user3) + assert User.following?(user2, user3) + + {:ok, user3} = User.follow(user3, user2) + assert User.following?(user3, user2) + + {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"}) + + {:ok, private_activity_1} = CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"}) + {:ok, private_activity_2} = CommonAPI.post(user2, %{"status" => "hi 3", "visibility" => "private", "in_reply_to_status_id" => private_activity_1.id}) + {:ok, private_activity_3} = CommonAPI.post(user3, %{"status" => "hi 4", "visibility" => "private", "in_reply_to_status_id" => private_activity_2.id}) + + assert user1.following == [user3.ap_id <> "/followers", user1.ap_id] + + activities = + ActivityPub.fetch_activities([user1.ap_id | user1.following]) + + assert [public_activity, private_activity_1, private_activity_3] == activities + assert length(activities) == 3 + + activities = ActivityPub.contain_timeline(activities, user1) + + assert [public_activity, private_activity_1] == activities + assert length(activities) == 2 + end + end + test "it can fetch plume articles" do {:ok, object} = ActivityPub.fetch_object_from_id( From 2ac13061afe4c360bb98ba8f05bc540b2cf27e74 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 26 Oct 2018 06:37:00 +0000 Subject: [PATCH 3/3] testsuite: formatting --- test/web/activity_pub/activity_pub_test.exs | 22 ++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 14011a010..35c381ac3 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -493,14 +493,26 @@ test "it filters broken threads" do {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"}) - {:ok, private_activity_1} = CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"}) - {:ok, private_activity_2} = CommonAPI.post(user2, %{"status" => "hi 3", "visibility" => "private", "in_reply_to_status_id" => private_activity_1.id}) - {:ok, private_activity_3} = CommonAPI.post(user3, %{"status" => "hi 4", "visibility" => "private", "in_reply_to_status_id" => private_activity_2.id}) + {:ok, private_activity_1} = + CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"}) + + {:ok, private_activity_2} = + CommonAPI.post(user2, %{ + "status" => "hi 3", + "visibility" => "private", + "in_reply_to_status_id" => private_activity_1.id + }) + + {:ok, private_activity_3} = + CommonAPI.post(user3, %{ + "status" => "hi 4", + "visibility" => "private", + "in_reply_to_status_id" => private_activity_2.id + }) assert user1.following == [user3.ap_id <> "/followers", user1.ap_id] - activities = - ActivityPub.fetch_activities([user1.ap_id | user1.following]) + activities = ActivityPub.fetch_activities([user1.ap_id | user1.following]) assert [public_activity, private_activity_1, private_activity_3] == activities assert length(activities) == 3