[#1364] Improved control over generation / sending of notifications. Fixed blocking / muting users notifications issue. Added tests.

This commit is contained in:
Ivan Tashkinov 2020-03-15 21:00:12 +03:00
parent d84670b9e1
commit 7c8003c3fc
7 changed files with 292 additions and 63 deletions

View file

@ -95,6 +95,16 @@ def with_preloaded_object(query, join_type \\ :inner) do
|> preload([activity, object: object], object: object) |> preload([activity, object: object], object: object)
end end
def user_actor(%Activity{actor: nil}), do: nil
def user_actor(%Activity{} = activity) do
with %User{} <- activity.user_actor do
activity.user_actor
else
_ -> User.get_cached_by_ap_id(activity.actor)
end
end
def with_joined_user_actor(query, join_type \\ :inner) do def with_joined_user_actor(query, join_type \\ :inner) do
join(query, join_type, [activity], u in User, join(query, join_type, [activity], u in User,
on: u.ap_id == activity.actor, on: u.ap_id == activity.actor,

View file

@ -10,6 +10,7 @@ defmodule Pleroma.Notification do
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Pagination alias Pleroma.Pagination
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.ThreadMute
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.Push alias Pleroma.Web.Push
@ -17,6 +18,7 @@ defmodule Pleroma.Notification do
import Ecto.Query import Ecto.Query
import Ecto.Changeset import Ecto.Changeset
require Logger require Logger
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@ -101,7 +103,7 @@ defp exclude_notification_muted(query, user, opts) do
query query
|> where([n, a], a.actor not in ^notification_muted_ap_ids) |> where([n, a], a.actor not in ^notification_muted_ap_ids)
|> join(:left, [n, a], tm in Pleroma.ThreadMute, |> 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)
) )
|> where([n, a, o, tm], is_nil(tm.user_id)) |> where([n, a, o, tm], is_nil(tm.user_id))
@ -284,58 +286,108 @@ def dismiss(%{id: user_id} = _user, id) do
def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity) do def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity) do
object = Object.normalize(activity) object = Object.normalize(activity)
unless object && object.data["type"] == "Answer" do if object && object.data["type"] == "Answer" do
users = get_notified_from_activity(activity)
notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
{:ok, notifications}
else
{:ok, []} {:ok, []}
else
do_create_notifications(activity)
end end
end end
def create_notifications(%Activity{data: %{"type" => type}} = activity) def create_notifications(%Activity{data: %{"type" => type}} = activity)
when type in ["Like", "Announce", "Follow", "Move", "EmojiReact"] do when type in ["Like", "Announce", "Follow", "Move", "EmojiReact"] do
notifications = do_create_notifications(activity)
activity
|> get_notified_from_activity()
|> Enum.map(&create_notification(activity, &1))
{:ok, notifications}
end end
def create_notifications(_), do: {:ok, []} def create_notifications(_), do: {:ok, []}
defp do_create_notifications(%Activity{} = activity) do
{enabled_receivers, disabled_receivers} = get_notified_from_activity(activity)
potential_receivers = enabled_receivers ++ disabled_receivers
notifications =
Enum.map(potential_receivers, fn user ->
do_send = user in enabled_receivers
create_notification(activity, user, do_send)
end)
{:ok, notifications}
end
# TODO move to sql, too. # TODO move to sql, too.
def create_notification(%Activity{} = activity, %User{} = user) do def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do
unless skip?(activity, user) do unless skip?(activity, user) do
notification = %Notification{user_id: user.id, activity: activity} notification = %Notification{user_id: user.id, activity: activity}
{:ok, notification} = Repo.insert(notification) {:ok, notification} = Repo.insert(notification)
["user", "user:notification"] if do_send do
|> Streamer.stream(notification) Streamer.stream(["user", "user:notification"], notification)
Push.send(notification) Push.send(notification)
end
notification notification
end end
end end
@doc """
Returns a tuple with 2 elements:
{enabled notification receivers, currently disabled receivers (blocking / [thread] muting)}
"""
def get_notified_from_activity(activity, local_only \\ true) def get_notified_from_activity(activity, local_only \\ true)
def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) 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"] do
potential_receiver_ap_ids =
[] []
|> Utils.maybe_notify_to_recipients(activity) |> Utils.maybe_notify_to_recipients(activity)
|> Utils.maybe_notify_mentioned_recipients(activity) |> Utils.maybe_notify_mentioned_recipients(activity)
|> Utils.maybe_notify_subscribers(activity) |> Utils.maybe_notify_subscribers(activity)
|> Utils.maybe_notify_followers(activity) |> Utils.maybe_notify_followers(activity)
|> Enum.uniq() |> Enum.uniq()
notification_enabled_ap_ids =
potential_receiver_ap_ids
|> exclude_relation_restricting_ap_ids(activity)
|> exclude_thread_muter_ap_ids(activity)
potential_receivers =
potential_receiver_ap_ids
|> Enum.uniq()
|> User.get_users_from_set(local_only) |> User.get_users_from_set(local_only)
notification_enabled_users =
Enum.filter(potential_receivers, fn u -> u.ap_id in notification_enabled_ap_ids end)
{notification_enabled_users, potential_receivers -- notification_enabled_users}
end end
def get_notified_from_activity(_, _local_only), do: [] def get_notified_from_activity(_, _local_only), do: {[], []}
@doc "Filters out AP IDs of users basing on their relationships with activity actor user"
def exclude_relation_restricting_ap_ids([], _activity), do: []
def exclude_relation_restricting_ap_ids(ap_ids, %Activity{} = activity) do
relation_restricted_ap_ids =
activity
|> Activity.user_actor()
|> User.incoming_relations_ungrouped_ap_ids([
:block,
:notification_mute
])
Enum.uniq(ap_ids) -- relation_restricted_ap_ids
end
@doc "Filters out AP IDs of users who mute activity thread"
def exclude_thread_muter_ap_ids([], _activity), do: []
def exclude_thread_muter_ap_ids(ap_ids, %Activity{} = activity) do
thread_muter_ap_ids = ThreadMute.muter_ap_ids(activity.data["context"])
Enum.uniq(ap_ids) -- thread_muter_ap_ids
end
@spec skip?(Activity.t(), User.t()) :: boolean() @spec skip?(Activity.t(), User.t()) :: boolean()
def skip?(activity, user) do def skip?(%Activity{} = activity, %User{} = user) do
[ [
:self, :self,
:followers, :followers,
@ -344,18 +396,20 @@ def skip?(activity, user) do
:non_follows, :non_follows,
:recently_followed :recently_followed
] ]
|> Enum.any?(&skip?(&1, activity, user)) |> Enum.find(&skip?(&1, activity, user))
end end
def skip?(_, _), do: false
@spec skip?(atom(), Activity.t(), User.t()) :: boolean() @spec skip?(atom(), Activity.t(), User.t()) :: boolean()
def skip?(:self, activity, user) do def skip?(:self, %Activity{} = activity, %User{} = user) do
activity.data["actor"] == user.ap_id activity.data["actor"] == user.ap_id
end end
def skip?( def skip?(
:followers, :followers,
activity, %Activity{} = activity,
%{notification_settings: %{followers: false}} = user %User{notification_settings: %{followers: false}} = user
) do ) do
actor = activity.data["actor"] actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor) follower = User.get_cached_by_ap_id(actor)
@ -364,15 +418,19 @@ def skip?(
def skip?( def skip?(
:non_followers, :non_followers,
activity, %Activity{} = activity,
%{notification_settings: %{non_followers: false}} = user %User{notification_settings: %{non_followers: false}} = user
) do ) do
actor = activity.data["actor"] actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor) follower = User.get_cached_by_ap_id(actor)
!User.following?(follower, user) !User.following?(follower, user)
end end
def skip?(:follows, activity, %{notification_settings: %{follows: false}} = user) do def skip?(
:follows,
%Activity{} = activity,
%User{notification_settings: %{follows: false}} = user
) do
actor = activity.data["actor"] actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor) followed = User.get_cached_by_ap_id(actor)
User.following?(user, followed) User.following?(user, followed)
@ -380,15 +438,16 @@ def skip?(:follows, activity, %{notification_settings: %{follows: false}} = user
def skip?( def skip?(
:non_follows, :non_follows,
activity, %Activity{} = activity,
%{notification_settings: %{non_follows: false}} = user %User{notification_settings: %{non_follows: false}} = user
) do ) do
actor = activity.data["actor"] actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor) followed = User.get_cached_by_ap_id(actor)
!User.following?(user, followed) !User.following?(user, followed)
end end
def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do # 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"] actor = activity.data["actor"]
Notification.for_user(user) Notification.for_user(user)

View file

@ -9,7 +9,8 @@ defmodule Pleroma.ThreadMute do
alias Pleroma.ThreadMute alias Pleroma.ThreadMute
alias Pleroma.User alias Pleroma.User
require Ecto.Query import Ecto.Changeset
import Ecto.Query
schema "thread_mutes" do schema "thread_mutes" do
belongs_to(:user, User, type: FlakeId.Ecto.CompatType) belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
@ -18,19 +19,43 @@ defmodule Pleroma.ThreadMute do
def changeset(mute, params \\ %{}) do def changeset(mute, params \\ %{}) do
mute mute
|> Ecto.Changeset.cast(params, [:user_id, :context]) |> cast(params, [:user_id, :context])
|> Ecto.Changeset.foreign_key_constraint(:user_id) |> foreign_key_constraint(:user_id)
|> Ecto.Changeset.unique_constraint(:user_id, name: :unique_index) |> unique_constraint(:user_id, name: :unique_index)
end end
def query(user_id, context) do def query(user_id, context) do
{:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id) {:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id)
ThreadMute ThreadMute
|> Ecto.Query.where(user_id: ^user_id) |> where(user_id: ^user_id)
|> Ecto.Query.where(context: ^context) |> where(context: ^context)
end end
def muters_query(context) do
ThreadMute
|> join(:inner, [tm], u in assoc(tm, :user))
|> where([tm], tm.context == ^context)
|> select([tm, u], u.ap_id)
end
def muter_ap_ids(context, ap_ids \\ nil)
def muter_ap_ids(context, ap_ids) when context not in [nil, ""] do
context
|> muters_query()
|> maybe_filter_on_ap_id(ap_ids)
|> Repo.all()
end
def muter_ap_ids(_context, _ap_ids), do: []
defp maybe_filter_on_ap_id(query, ap_ids) when is_list(ap_ids) do
where(query, [tm, u], u.ap_id in ^ap_ids)
end
defp maybe_filter_on_ap_id(query, _ap_ids), do: query
def add_mute(user_id, context) do def add_mute(user_id, context) do
%ThreadMute{} %ThreadMute{}
|> changeset(%{user_id: user_id, context: context}) |> changeset(%{user_id: user_id, context: context})

View file

@ -149,22 +149,26 @@ defmodule Pleroma.User do
{outgoing_relation, outgoing_relation_target}, {outgoing_relation, outgoing_relation_target},
{incoming_relation, incoming_relation_source} {incoming_relation, incoming_relation_source}
]} <- @user_relationships_config do ]} <- @user_relationships_config do
# Definitions of `has_many :blocker_blocks`, `has_many :muter_mutes` etc. # Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
# :notification_muter_mutes, :subscribee_subscriptions
has_many(outgoing_relation, UserRelationship, has_many(outgoing_relation, UserRelationship,
foreign_key: :source_id, foreign_key: :source_id,
where: [relationship_type: relationship_type] where: [relationship_type: relationship_type]
) )
# Definitions of `has_many :blockee_blocks`, `has_many :mutee_mutes` etc. # Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
# :notification_mutee_mutes, :subscriber_subscriptions
has_many(incoming_relation, UserRelationship, has_many(incoming_relation, UserRelationship,
foreign_key: :target_id, foreign_key: :target_id,
where: [relationship_type: relationship_type] where: [relationship_type: relationship_type]
) )
# Definitions of `has_many :blocked_users`, `has_many :muted_users` etc. # Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
# :notification_muted_users, :subscriber_users
has_many(outgoing_relation_target, through: [outgoing_relation, :target]) has_many(outgoing_relation_target, through: [outgoing_relation, :target])
# Definitions of `has_many :blocker_users`, `has_many :muter_users` etc. # Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
# :notification_muter_users, :subscribee_users
has_many(incoming_relation_source, through: [incoming_relation, :source]) has_many(incoming_relation_source, through: [incoming_relation, :source])
end end
@ -184,7 +188,9 @@ defmodule Pleroma.User do
for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <- for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
@user_relationships_config do @user_relationships_config do
# Definitions of `blocked_users_relation/1`, `muted_users_relation/1`, etc. # `def blocked_users_relation/2`, `def muted_users_relation/2`,
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
# `def subscriber_users/2`
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
target_users_query = assoc(user, unquote(outgoing_relation_target)) target_users_query = assoc(user, unquote(outgoing_relation_target))
@ -195,7 +201,8 @@ def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated?
end end
end end
# Definitions of `blocked_users/1`, `muted_users/1`, etc. # `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
# `def notification_muted_users/2`, `def subscriber_users/2`
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
__MODULE__ __MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [ |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@ -205,7 +212,8 @@ def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|> Repo.all() |> Repo.all()
end end
# Definitions of `blocked_users_ap_ids/1`, `muted_users_ap_ids/1`, etc. # `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
# `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
__MODULE__ __MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [ |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@ -1217,7 +1225,9 @@ def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
E.g. `outgoing_relations_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}` E.g. `outgoing_relations_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
""" """
@spec outgoing_relations_ap_ids(User.t(), list(atom())) :: %{atom() => list(String.t())} @spec outgoing_relations_ap_ids(User.t(), list(atom())) :: %{atom() => list(String.t())}
def outgoing_relations_ap_ids(_, []), do: %{} def outgoing_relations_ap_ids(_user, []), do: %{}
def outgoing_relations_ap_ids(nil, _relationship_types), do: %{}
def outgoing_relations_ap_ids(%User{} = user, relationship_types) def outgoing_relations_ap_ids(%User{} = user, relationship_types)
when is_list(relationship_types) do when is_list(relationship_types) do
@ -1238,6 +1248,30 @@ def outgoing_relations_ap_ids(%User{} = user, relationship_types)
) )
end end
def incoming_relations_ungrouped_ap_ids(user, relationship_types, ap_ids \\ nil)
def incoming_relations_ungrouped_ap_ids(_user, [], _ap_ids), do: []
def incoming_relations_ungrouped_ap_ids(nil, _relationship_types, _ap_ids), do: []
def incoming_relations_ungrouped_ap_ids(%User{} = user, relationship_types, ap_ids)
when is_list(relationship_types) do
user
|> assoc(:incoming_relationships)
|> join(:inner, [user_rel], u in assoc(user_rel, :source))
|> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
|> maybe_filter_on_ap_id(ap_ids)
|> select([user_rel, u], u.ap_id)
|> distinct(true)
|> Repo.all()
end
defp maybe_filter_on_ap_id(query, ap_ids) when is_list(ap_ids) do
where(query, [user_rel, u], u.ap_id in ^ap_ids)
end
defp maybe_filter_on_ap_id(query, _ap_ids), do: query
def deactivate_async(user, status \\ true) do def deactivate_async(user, status \\ true) do
BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status}) BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status})
end end

View file

@ -21,15 +21,18 @@ defmodule Pleroma.UserRelationship do
end end
for relationship_type <- Keyword.keys(UserRelationshipTypeEnum.__enum_map__()) do for relationship_type <- Keyword.keys(UserRelationshipTypeEnum.__enum_map__()) do
# Definitions of `create_block/2`, `create_mute/2` etc. # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
# `def create_notification_mute/2`, `def create_inverse_subscription/2`
def unquote(:"create_#{relationship_type}")(source, target), def unquote(:"create_#{relationship_type}")(source, target),
do: create(unquote(relationship_type), source, target) do: create(unquote(relationship_type), source, target)
# Definitions of `delete_block/2`, `delete_mute/2` etc. # `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
# `def delete_notification_mute/2`, `def delete_inverse_subscription/2`
def unquote(:"delete_#{relationship_type}")(source, target), def unquote(:"delete_#{relationship_type}")(source, target),
do: delete(unquote(relationship_type), source, target) do: delete(unquote(relationship_type), source, target)
# Definitions of `block_exists?/2`, `mute_exists?/2` etc. # `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
# `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`
def unquote(:"#{relationship_type}_exists?")(source, target), def unquote(:"#{relationship_type}_exists?")(source, target),
do: exists?(unquote(relationship_type), source, target) do: exists?(unquote(relationship_type), source, target)
end end

View file

@ -1108,13 +1108,11 @@ def add_hashtags(object) do
end end
def add_mention_tags(object) do def add_mention_tags(object) do
mentions = {enabled_receivers, disabled_receivers} = Utils.get_notified_from_object(object)
object potential_receivers = enabled_receivers ++ disabled_receivers
|> Utils.get_notified_from_object() mentions = Enum.map(potential_receivers, &build_mention_tag/1)
|> Enum.map(&build_mention_tag/1)
tags = object["tag"] || [] tags = object["tag"] || []
Map.put(object, "tag", tags ++ mentions) Map.put(object, "tag", tags ++ mentions)
end end

View file

@ -6,12 +6,14 @@ defmodule Pleroma.NotificationTest do
use Pleroma.DataCase use Pleroma.DataCase
import Pleroma.Factory import Pleroma.Factory
import Mock
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Tests.ObanHelpers alias Pleroma.Tests.ObanHelpers
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Push
alias Pleroma.Web.Streamer alias Pleroma.Web.Streamer
describe "create_notifications" do describe "create_notifications" do
@ -382,7 +384,7 @@ test "Returns recent notifications" do
end end
end end
describe "notification target determination" do describe "notification target determination / get_notified_from_activity/2" do
test "it sends notifications to addressed users in new messages" do test "it sends notifications to addressed users in new messages" do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
@ -392,7 +394,9 @@ test "it sends notifications to addressed users in new messages" do
"status" => "hey @#{other_user.nickname}!" "status" => "hey @#{other_user.nickname}!"
}) })
assert other_user in Notification.get_notified_from_activity(activity) {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
assert other_user in enabled_receivers
end end
test "it sends notifications to mentioned users in new messages" do test "it sends notifications to mentioned users in new messages" do
@ -420,7 +424,9 @@ test "it sends notifications to mentioned users in new messages" do
{:ok, activity} = Transmogrifier.handle_incoming(create_activity) {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
assert other_user in Notification.get_notified_from_activity(activity) {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
assert other_user in enabled_receivers
end end
test "it does not send notifications to users who are only cc in new messages" do test "it does not send notifications to users who are only cc in new messages" do
@ -442,7 +448,9 @@ test "it does not send notifications to users who are only cc in new messages" d
{:ok, activity} = Transmogrifier.handle_incoming(create_activity) {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
assert other_user not in Notification.get_notified_from_activity(activity) {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
assert other_user not in enabled_receivers
end end
test "it does not send notification to mentioned users in likes" do test "it does not send notification to mentioned users in likes" do
@ -457,7 +465,10 @@ test "it does not send notification to mentioned users in likes" do
{:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user) {:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user)
assert other_user not in Notification.get_notified_from_activity(activity_two) {enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(activity_two)
assert other_user not in enabled_receivers
end end
test "it does not send notification to mentioned users in announces" do test "it does not send notification to mentioned users in announces" do
@ -472,7 +483,96 @@ test "it does not send notification to mentioned users in announces" do
{:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user) {:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user)
assert other_user not in Notification.get_notified_from_activity(activity_two) {enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(activity_two)
assert other_user not in enabled_receivers
end
test_with_mock "it returns blocking recipient in disabled recipients list",
Push,
[:passthrough],
[] do
user = insert(:user)
other_user = insert(:user)
{:ok, _user_relationship} = User.block(other_user, user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
{enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
assert [] == enabled_receivers
assert [other_user] == disabled_receivers
assert 1 == length(Repo.all(Notification))
refute called(Push.send(:_))
end
test_with_mock "it returns notification-muting recipient in disabled recipients list",
Push,
[:passthrough],
[] do
user = insert(:user)
other_user = insert(:user)
{:ok, _user_relationships} = User.mute(other_user, user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
{enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
assert [] == enabled_receivers
assert [other_user] == disabled_receivers
assert 1 == length(Repo.all(Notification))
refute called(Push.send(:_))
end
test_with_mock "it returns thread-muting recipient in disabled recipients list",
Push,
[:passthrough],
[] do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
{:ok, _} = CommonAPI.add_mute(other_user, activity)
{:ok, same_context_activity} =
CommonAPI.post(user, %{
"status" => "hey-hey-hey @#{other_user.nickname}!",
"in_reply_to_status_id" => activity.id
})
{enabled_receivers, disabled_receivers} =
Notification.get_notified_from_activity(same_context_activity)
assert [other_user] == disabled_receivers
refute other_user in enabled_receivers
[pre_mute_notification, post_mute_notification] =
Repo.all(from(n in Notification, where: n.user_id == ^other_user.id, order_by: n.id))
pre_mute_notification_id = pre_mute_notification.id
post_mute_notification_id = post_mute_notification.id
assert called(
Push.send(
:meck.is(fn
%Notification{id: ^pre_mute_notification_id} -> true
_ -> false
end)
)
)
refute called(
Push.send(
:meck.is(fn
%Notification{id: ^post_mute_notification_id} -> true
_ -> false
end)
)
)
end end
end end