X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fnotification.ex;h=32bcfcaba34d9c830211c3f8180481feff193b5d;hb=762be6ce10d2145e8e31d42c5d1a0bab93dbe7b0;hp=9ee9606becb9fd9bf779d81b6b72a01c5b4595ac;hpb=4ec2fb967e0eb0559e39a6d698107f6af4d7a891;p=akkoma diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 9ee9606be..4cc9a5669 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Notification do @@ -15,6 +15,7 @@ defmodule Pleroma.Notification do alias Pleroma.Repo alias Pleroma.ThreadMute alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.Push alias Pleroma.Web.Streamer @@ -69,6 +70,7 @@ defmodule Pleroma.Notification do move pleroma:chat_mention pleroma:emoji_reaction + pleroma:report reblog } @@ -110,13 +112,6 @@ defmodule Pleroma.Notification do Notification |> where(user_id: ^user.id) - |> where( - [n, a], - fragment( - "? not in (SELECT ap_id FROM users WHERE deactivated = 'true')", - a.actor - ) - ) |> join(:inner, [n], activity in assoc(n, :activity)) |> join(:left, [n, a], object in Object, on: @@ -127,9 +122,13 @@ defmodule Pleroma.Notification do a.data ) ) + |> join(:inner, [_n, a], u in User, on: u.ap_id == a.actor, as: :user_actor) |> preload([n, a, o], activity: {a, object: o}) + |> where([user_actor: user_actor], user_actor.is_active) |> exclude_notification_muted(user, exclude_notification_muted_opts) |> exclude_blocked(user, exclude_blocked_opts) + |> exclude_blockers(user) + |> exclude_filtered(user) |> exclude_visibility(opts) end @@ -142,6 +141,17 @@ defmodule Pleroma.Notification do |> FollowingRelationship.keep_following_or_not_domain_blocked(user) end + defp exclude_blockers(query, user) do + if Pleroma.Config.get([:activitypub, :blockers_visible]) == true do + query + else + blocker_ap_ids = User.incoming_relationships_ungrouped_ap_ids(user, [:block]) + + query + |> where([n, a], a.actor not in ^blocker_ap_ids) + end + end + defp exclude_notification_muted(query, _, %{@include_muted_option => true}) do query end @@ -153,9 +163,24 @@ defmodule Pleroma.Notification do query |> where([n, a], a.actor not in ^notification_muted_ap_ids) |> join(:left, [n, a], tm in ThreadMute, - on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data) + on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data), + as: :thread_mute ) - |> where([n, a, o, tm], is_nil(tm.user_id)) + |> where([thread_mute: thread_mute], is_nil(thread_mute.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] @@ -337,10 +362,11 @@ defmodule Pleroma.Notification do end end + @spec create_notifications(Activity.t(), keyword()) :: {:ok, [Notification.t()] | []} def create_notifications(activity, options \\ []) def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity, options) do - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) if object && object.data["type"] == "Answer" do {:ok, []} @@ -350,7 +376,7 @@ defmodule Pleroma.Notification do end def create_notifications(%Activity{data: %{"type" => type}} = activity, options) - when type in ["Follow", "Like", "Announce", "Move", "EmojiReact"] do + when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag"] do do_create_notifications(activity, options) end @@ -367,6 +393,7 @@ defmodule Pleroma.Notification do do_send = do_send && user in enabled_receivers create_notification(activity, user, do_send) end) + |> Enum.reject(&is_nil/1) {:ok, notifications} end @@ -392,6 +419,9 @@ defmodule Pleroma.Notification do "EmojiReact" -> "pleroma:emoji_reaction" + "Flag" -> + "pleroma:report" + # Compatibility with old reactions "EmojiReaction" -> "pleroma:emoji_reaction" @@ -424,6 +454,7 @@ defmodule Pleroma.Notification do |> Multi.insert(:notification, %Notification{ user_id: user.id, activity: activity, + seen: mark_as_read?(activity, user), type: type_from_activity(activity) }) |> Marker.multi_set_last_read_id(user, "notifications") @@ -448,7 +479,7 @@ defmodule Pleroma.Notification do def get_notified_from_activity(activity, local_only \\ true) def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) - when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do + when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag"] do potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity) potential_receivers = @@ -480,6 +511,14 @@ defmodule Pleroma.Notification do end end + def get_potential_receiver_ap_ids(%{data: %{"type" => "Follow", "object" => object_id}}) do + [object_id] + end + + def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do + (User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor] + end + def get_potential_receiver_ap_ids(activity) do [] |> Utils.maybe_notify_to_recipients(activity) @@ -550,11 +589,9 @@ defmodule Pleroma.Notification do [ :self, :invisible, - :followers, - :follows, - :non_followers, - :non_follows, - :recently_followed + :block_from_strangers, + :recently_followed, + :filtered ] |> Enum.find(&skip?(&1, activity, user)) end @@ -573,45 +610,15 @@ defmodule Pleroma.Notification do end def skip?( - :followers, - %Activity{} = activity, - %User{notification_settings: %{followers: false}} = user - ) do - actor = activity.data["actor"] - follower = User.get_cached_by_ap_id(actor) - User.following?(follower, user) - end - - def skip?( - :non_followers, + :block_from_strangers, %Activity{} = activity, - %User{notification_settings: %{non_followers: false}} = user + %User{notification_settings: %{block_from_strangers: true}} = user ) do actor = activity.data["actor"] follower = User.get_cached_by_ap_id(actor) !User.following?(follower, user) end - def skip?( - :follows, - %Activity{} = activity, - %User{notification_settings: %{follows: false}} = user - ) do - actor = activity.data["actor"] - followed = User.get_cached_by_ap_id(actor) - User.following?(user, followed) - end - - def skip?( - :non_follows, - %Activity{} = activity, - %User{notification_settings: %{non_follows: false}} = user - ) do - actor = activity.data["actor"] - followed = User.get_cached_by_ap_id(actor) - !User.following?(user, followed) - end - # To do: consider defining recency in hours and checking FollowingRelationship with a single SQL def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity, %User{} = user) do actor = activity.data["actor"] @@ -623,8 +630,33 @@ defmodule Pleroma.Notification do end) end + def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false + + def skip?(:filtered, activity, user) do + object = Object.normalize(activity, fetch: false) + + 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 mark_as_read?(activity, target_user) do + user = Activity.user_actor(activity) + User.mutes_user?(target_user, user) || CommonAPI.thread_muted?(target_user, activity) + end + def for_user_and_activity(user, activity) do from(n in __MODULE__, where: n.user_id == ^user.id, @@ -632,4 +664,16 @@ defmodule Pleroma.Notification do ) |> Repo.one() end + + @spec mark_context_as_read(User.t(), String.t()) :: {integer(), nil | [term()]} + def mark_context_as_read(%User{id: id}, context) do + from( + n in Notification, + join: a in assoc(n, :activity), + where: n.user_id == ^id, + where: n.seen == false, + where: fragment("?->>'context'", a.data) == ^context + ) + |> Repo.update_all(set: [seen: true]) + end end