From 5af1bf443dfd21a6b0be9efc1f55a73e590f6ba3 Mon Sep 17 00:00:00 2001 From: Sergey Suprunenko Date: Fri, 22 Nov 2019 19:52:50 +0100 Subject: [PATCH] Skip notifications for statuses that contain an irreversible filtered word --- lib/pleroma/notification.ex | 36 ++++++++++++- test/notification_test.exs | 101 ++++++++++++++++++++++++++++++------ 2 files changed, 119 insertions(+), 18 deletions(-) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 2ef1a80c5..3f749cace 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -130,6 +130,7 @@ def for_user_query(user, opts \\ %{}) do |> preload([n, a, o], activity: {a, object: o}) |> exclude_notification_muted(user, exclude_notification_muted_opts) |> exclude_blocked(user, exclude_blocked_opts) + |> exclude_filtered(user) |> exclude_visibility(opts) end @@ -158,6 +159,20 @@ defp exclude_notification_muted(query, user, opts) do |> where([n, a, o, tm], is_nil(tm.user_id)) end + defp exclude_filtered(query, user) do + case Pleroma.Filter.compose_regex(user) do + nil -> + query + + regex -> + from([_n, a, o] in query, + where: + fragment("not(?->>'content' ~* ?)", o.data, ^regex) or + fragment("?->>'actor' = ?", o.data, ^user.ap_id) + ) + end + end + @valid_visibilities ~w[direct unlisted public private] defp exclude_visibility(query, %{exclude_visibilities: visibility}) @@ -555,7 +570,8 @@ def skip?(%Activity{} = activity, %User{} = user) do :follows, :non_followers, :non_follows, - :recently_followed + :recently_followed, + :filtered ] |> Enum.find(&skip?(&1, activity, user)) end @@ -624,6 +640,24 @@ def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity, end) end + def skip?(:filtered, activity, user) do + object = Object.normalize(activity) + + cond do + is_nil(object) -> + false + + object.data["actor"] == user.ap_id -> + false + + not is_nil(regex = Pleroma.Filter.compose_regex(user, :re)) -> + Regex.match?(regex, object.data["content"]) + + true -> + false + end + end + def skip?(_, _, _), do: false def for_user_and_activity(user, activity) do diff --git a/test/notification_test.exs b/test/notification_test.exs index 6add3f7eb..9ac6925c3 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -324,6 +324,44 @@ test "it disables notifications from people who are invisible" do {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"}) refute Notification.create_notification(status, user) end + + test "it doesn't create notifications if content matches with an irreversible filter" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + insert(:filter, user: subscriber, phrase: "cofe", hide: true) + + {:ok, status} = CommonAPI.post(user, %{"status" => "got cofe?"}) + + assert {:ok, [nil]} == Notification.create_notifications(status) + end + + test "it creates notifications if content matches with a not irreversible filter" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + insert(:filter, user: subscriber, phrase: "cofe", hide: false) + + {:ok, status} = CommonAPI.post(user, %{"status" => "got cofe?"}) + {:ok, [notification]} = Notification.create_notifications(status) + + assert notification + end + + test "it creates notifications when someone likes user's status with a filtered word" do + user = insert(:user) + other_user = insert(:user) + insert(:filter, user: user, phrase: "tesla", hide: true) + + {:ok, activity_one} = CommonAPI.post(user, %{"status" => "wow tesla"}) + {:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, other_user) + + {:ok, [notification]} = Notification.create_notifications(activity_two) + + assert notification + end end describe "follow / follow_request notifications" do @@ -990,8 +1028,13 @@ test "move activity generates a notification" do end describe "for_user" do - test "it returns notifications for muted user without notifications" do + setup do user = insert(:user) + + {:ok, %{user: user}} + end + + test "it returns notifications for muted user without notifications", %{user: user} do muted = insert(:user) {:ok, _user_relationships} = User.mute(user, muted, false) @@ -1002,8 +1045,7 @@ test "it returns notifications for muted user without notifications" do assert notification.activity.object end - test "it doesn't return notifications for muted user with notifications" do - user = insert(:user) + test "it doesn't return notifications for muted user with notifications", %{user: user} do muted = insert(:user) {:ok, _user_relationships} = User.mute(user, muted) @@ -1012,8 +1054,7 @@ test "it doesn't return notifications for muted user with notifications" do assert Notification.for_user(user) == [] end - test "it doesn't return notifications for blocked user" do - user = insert(:user) + test "it doesn't return notifications for blocked user", %{user: user} do blocked = insert(:user) {:ok, _user_relationship} = User.block(user, blocked) @@ -1022,8 +1063,7 @@ test "it doesn't return notifications for blocked user" do assert Notification.for_user(user) == [] end - test "it doesn't return notifications for domain-blocked non-followed user" do - user = insert(:user) + test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do blocked = insert(:user, ap_id: "http://some-domain.com") {:ok, user} = User.block_domain(user, "some-domain.com") @@ -1044,8 +1084,7 @@ test "it returns notifications for domain-blocked but followed user" do assert length(Notification.for_user(user)) == 1 end - test "it doesn't return notifications for muted thread" do - user = insert(:user) + test "it doesn't return notifications for muted thread", %{user: user} do another_user = insert(:user) {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) @@ -1054,8 +1093,7 @@ test "it doesn't return notifications for muted thread" do assert Notification.for_user(user) == [] end - test "it returns notifications from a muted user when with_muted is set" do - user = insert(:user) + test "it returns notifications from a muted user when with_muted is set", %{user: user} do muted = insert(:user) {:ok, _user_relationships} = User.mute(user, muted) @@ -1064,8 +1102,9 @@ test "it returns notifications from a muted user when with_muted is set" do assert length(Notification.for_user(user, %{with_muted: true})) == 1 end - test "it doesn't return notifications from a blocked user when with_muted is set" do - user = insert(:user) + test "it doesn't return notifications from a blocked user when with_muted is set", %{ + user: user + } do blocked = insert(:user) {:ok, _user_relationship} = User.block(user, blocked) @@ -1075,8 +1114,8 @@ test "it doesn't return notifications from a blocked user when with_muted is set end test "when with_muted is set, " <> - "it doesn't return notifications from a domain-blocked non-followed user" do - user = insert(:user) + "it doesn't return notifications from a domain-blocked non-followed user", + %{user: user} do blocked = insert(:user, ap_id: "http://some-domain.com") {:ok, user} = User.block_domain(user, "some-domain.com") @@ -1085,8 +1124,7 @@ test "when with_muted is set, " <> assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) end - test "it returns notifications from muted threads when with_muted is set" do - user = insert(:user) + test "it returns notifications from muted threads when with_muted is set", %{user: user} do another_user = insert(:user) {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) @@ -1094,5 +1132,34 @@ test "it returns notifications from muted threads when with_muted is set" do {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) assert length(Notification.for_user(user, %{with_muted: true})) == 1 end + + test "it doesn't return notifications about mentiones with filtered word", %{user: user} do + insert(:filter, user: user, phrase: "cofe", hide: true) + another_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(another_user, %{"status" => "@#{user.nickname} got cofe?"}) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "it returns notifications about mentiones with not hidden filtered word", %{user: user} do + insert(:filter, user: user, phrase: "test", hide: false) + another_user = insert(:user) + + {:ok, _activity} = CommonAPI.post(another_user, %{"status" => "@#{user.nickname} test"}) + + assert length(Notification.for_user(user)) == 1 + end + + test "it returns notifications about favorites with filtered word", %{user: user} do + insert(:filter, user: user, phrase: "cofe", hide: true) + another_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "Give me my cofe!"}) + {:ok, _, _} = CommonAPI.favorite(activity.id, another_user) + + assert length(Notification.for_user(user)) == 1 + end end end