unify notification settings

This commit is contained in:
Alexander Strizhakov 2020-06-16 08:41:57 +03:00
parent 7f413139fb
commit 16bcffd64a
No known key found for this signature in database
GPG Key ID: 022896A53AEF1381
15 changed files with 283 additions and 19 deletions

View File

@ -334,6 +334,12 @@ The message payload consist of:
Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
## Subscriptions
Has additional field in parameters:
- `pleroma:emoji_reaction`: Receive emoji reaction notifications?
## Not implemented
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.

View File

@ -301,12 +301,20 @@ See [Admin-API](admin_api.md)
Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.
## `/api/v1/pleroma/notification_settings`
### Updates user notification settings
* Method `PUT`
* Authentication: required
* Params:
* `block_from_strangers`: BOOLEAN field, blocks notifications from accounts you do not follow
* `hide_notification_contents`: BOOLEAN field. When set to true, it removes the contents of a message from the push notification.
* `block_from_strangers`: BOOLEAN field, blocks notifications from accounts you do not follow
* `hide_notification_contents`: BOOLEAN field. When set to true, it removes the contents of a message from the push notification.
* `followers`: BOOLEAN field, receives notifications from followers
* `follows`: BOOLEAN field, receives notifications from people the user follows
* `remote`: BOOLEAN field, receives notifications from people on remote instances
* `local`: BOOLEAN field, receives notifications from people on the local instance
* `privacy_option`: BOOLEAN field. When set to true, it removes the contents of a message from the push notification.
* `exclude_types`: ARRAY field. What notification types to exclude.
* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
## `/api/v1/pleroma/healthcheck`

View File

@ -74,6 +74,8 @@ defmodule Pleroma.Notification do
reblog
}
def types, do: @notification_types
def changeset(%Notification{} = notification, attrs) do
notification
|> cast(attrs, [:seen, :type])

View File

@ -12,13 +12,25 @@ defmodule Pleroma.User.NotificationSetting do
embedded_schema do
field(:block_from_strangers, :boolean, default: false)
field(:hide_notification_contents, :boolean, default: false)
field(:followers, :boolean, default: true)
field(:follows, :boolean, default: true)
field(:non_follows, :boolean, default: true)
field(:non_followers, :boolean, default: true)
field(:privacy_option, :boolean, default: false)
field(:exclude_types, {:array, :string}, default: [])
end
def changeset(schema, params) do
schema
|> cast(prepare_attrs(params), [
:block_from_strangers,
:hide_notification_contents
:hide_notification_contents,
:followers,
:follows,
:non_follows,
:non_followers,
:privacy_option,
:exclude_types
])
end

View File

@ -53,7 +53,24 @@ defmodule Pleroma.Web.MastoFEController do
@doc "PUT /api/web/settings: Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere"
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
with {:ok, _} <- User.mastodon_settings_update(user, settings) do
with {:ok, user} <- User.mastodon_settings_update(user, settings) do
if settings = get_in(user.settings, ["notifications", "shows"]) do
notify_settings =
Enum.map(settings, fn {k, v} ->
if v == false do
k
end
end)
|> Enum.filter(& &1)
notification_settings =
user.notification_settings
|> Map.from_struct()
|> Map.put(:exclude_types, notify_settings)
User.update_notification_settings(user, notification_settings)
end
json(conn, %{})
else
e ->

View File

@ -52,6 +52,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
def get_notifications(user, params \\ %{}) do
options = cast_params(params)
user_exclude_types = user.notification_settings.exclude_types
options =
if (!options[:exclude_types] or options[:exclude_types] == []) and user_exclude_types != [] do
Map.put(options, :exclude_types, user_exclude_types)
else
options
end
user
|> Notification.for_user_query(options)
|> restrict(:include_types, options)

View File

@ -19,7 +19,7 @@ defmodule Pleroma.Web.Push.Impl do
@types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact"]
@doc "Performs sending notifications for user subscriptions"
@spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type}
@spec perform(Notification.t()) :: {:ok, list(any)} | {:error, :unknown_type}
def perform(
%{
activity: %{data: %{"type" => activity_type}} = activity,
@ -164,13 +164,14 @@ defmodule Pleroma.Web.Push.Impl do
_object,
mastodon_type
)
when type in ["Follow", "Like"] do
when type in ["Follow", "Like", "EmojiReact"] do
mastodon_type = mastodon_type || notification.type
case mastodon_type do
"follow" -> "@#{actor.nickname} has followed you"
"follow_request" -> "@#{actor.nickname} has requested to follow you"
"favourite" -> "@#{actor.nickname} has favorited your post"
"pleroma:emoji_reaction" -> "@#{actor.nickname} has reacted to your post"
end
end

View File

@ -29,7 +29,8 @@ defmodule Pleroma.Web.Push.Subscription do
@supported_alert_types ~w[follow favourite mention reblog pleroma:chat_mention pleroma:emoji_reaction]a
defp alerts(%{data: %{alerts: alerts}}) do
alerts = Map.take(alerts, @supported_alert_types)
types = Enum.map(Pleroma.Notification.types(), &String.to_atom/1)
alerts = Map.take(alerts, types)
%{"alerts" => alerts}
end

View File

@ -171,8 +171,15 @@ defmodule Pleroma.Web.Streamer do
end
end
def filtered_by_user?(%User{} = user, %Notification{activity: activity}, _) do
filtered_by_user?(user, activity, :notification)
def filtered_by_user?(
%User{} = user,
%Notification{activity: activity, type: notification_type},
_
) do
notification_settings = user.notification_settings
notification_type not in notification_settings.exclude_types and
filtered_by_user?(user, activity, :notification)
end
defp do_stream("direct", item) do

View File

@ -0,0 +1,11 @@
defmodule Pleroma.Repo.Migrations.AddExcludeTypesIntoUserNotificationSettings do
use Ecto.Migration
def up do
execute("""
UPDATE users SET notification_settings = jsonb_set(notification_settings, '{exclude_types}', '[]') WHERE local = true;
""")
end
def down, do: :ok
end

View File

@ -408,6 +408,83 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200)
end
defp update_notification_settings_and_conn(user, conn, exclude_types) do
{:ok, user} =
User.update_notification_settings(user, %{
"exclude_types" => exclude_types
})
conn
|> assign(:user, user)
|> get("/api/v1/notifications")
end
test "filters notifications with user settings for exclude_types" do
%{user: user, conn: conn} = oauth_access(["read:notifications"])
other_user = insert(:user)
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
mention_notification_id = get_notification_id_by_activity(mention_activity)
favorite_notification_id = get_notification_id_by_activity(favorite_activity)
reblog_notification_id = get_notification_id_by_activity(reblog_activity)
follow_notification_id = get_notification_id_by_activity(follow_activity)
conn_res =
update_notification_settings_and_conn(user, conn, ["mention", "favourite", "reblog"])
assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200)
conn_res =
update_notification_settings_and_conn(user, conn, ["favourite", "reblog", "follow"])
assert [%{"id" => ^mention_notification_id}] =
json_response_and_validate_schema(conn_res, 200)
conn_res = update_notification_settings_and_conn(user, conn, ["reblog", "follow", "mention"])
assert [%{"id" => ^favorite_notification_id}] =
json_response_and_validate_schema(conn_res, 200)
conn_res =
update_notification_settings_and_conn(user, conn, ["follow", "mention", "favourite"])
assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200)
end
test "exclude_types params have high priority than user settings" do
%{user: user, conn: conn} = oauth_access(["read:notifications"])
other_user = insert(:user)
{:ok, _mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
{:ok, _favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, _reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
follow_notification_id = get_notification_id_by_activity(follow_activity)
{:ok, user} =
User.update_notification_settings(user, %{
"exclude_types" => ["favourite", "reblog", "follow", "mention"]
})
query = params_to_query(%{exclude_types: ["mention", "favourite", "reblog"]})
conn_res =
conn
|> assign(:user, user)
|> get("/api/v1/notifications?" <> query)
assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200)
end
test "filters notifications using include_types" do
%{user: user, conn: conn} = oauth_access(["read:notifications"])
other_user = insert(:user)

View File

@ -11,19 +11,96 @@ defmodule Pleroma.Web.MastodonAPI.MastoFEControllerTest do
setup do: clear_config([:instance, :public])
test "put settings", %{conn: conn} do
user = insert(:user)
describe "put_settings/2" do
setup do
%{conn: conn, user: user} = oauth_access(["write:accounts"])
[conn: conn, user: user]
end
conn =
conn
|> assign(:user, user)
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:accounts"]))
|> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
test "common", %{conn: conn, user: user} do
assert conn
|> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
|> json_response(200)
assert _result = json_response(conn, 200)
user = User.get_cached_by_ap_id(user.ap_id)
assert user.mastofe_settings == %{"programming" => "socks"}
end
user = User.get_cached_by_ap_id(user.ap_id)
assert user.mastofe_settings == %{"programming" => "socks"}
test "saves notification settings", %{conn: conn, user: user} do
assert conn
|> put("/api/web/settings", %{
"data" => %{
"notifications" => %{
"alerts" => %{
"favourite" => true,
"follow" => true,
"follow_request" => true,
"mention" => true,
"poll" => true,
"reblog" => true
},
"quickFilter" => %{"active" => "all", "advanced" => true, "show" => true},
"shows" => %{
"favourite" => false,
"follow" => false,
"follow_request" => false,
"mention" => false,
"poll" => false,
"reblog" => false
},
"sounds" => %{
"favourite" => true,
"follow" => true,
"follow_request" => true,
"mention" => true,
"poll" => true,
"reblog" => true
}
}
}
})
user = User.get_cached_by_ap_id(user.ap_id)
assert user.settings == %{
"notifications" => %{
"alerts" => %{
"favourite" => true,
"follow" => true,
"follow_request" => true,
"mention" => true,
"poll" => true,
"reblog" => true
},
"quickFilter" => %{"active" => "all", "advanced" => true, "show" => true},
"shows" => %{
"favourite" => false,
"follow" => false,
"follow_request" => false,
"mention" => false,
"poll" => false,
"reblog" => false
},
"sounds" => %{
"favourite" => true,
"follow" => true,
"follow_request" => true,
"mention" => true,
"poll" => true,
"reblog" => true
}
}
}
assert user.notification_settings.exclude_types == [
"favourite",
"follow",
"follow_request",
"mention",
"poll",
"reblog"
]
end
end
describe "index/2 redirections" do

View File

@ -125,6 +125,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
notification_settings = %{
block_from_strangers: false,
hide_notification_contents: false
followers: true,
follows: true,
non_followers: true,
non_follows: true,
privacy_option: false,
exclude_types: []
}
privacy = user.default_scope

View File

@ -69,6 +69,27 @@ defmodule Pleroma.Web.Push.ImplTest do
assert Impl.perform(notif) == {:ok, [:ok, :ok]}
end
test "notification for follow_request" do
user = insert(:user)
reaction_user = insert(:user)
insert(:push_subscription, user: user, data: %{alerts: %{"pleroma:emoji_reaction" => true}})
insert(:push_subscription, user: user, data: %{alerts: %{"pleroma:emoji_reaction" => false}})
{:ok, activity} = CommonAPI.post(user, %{status: "<Lorem ipsum dolor sit amet."})
{:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, reaction_user, "")
notif =
insert(:notification,
user: user,
activity: reaction_activity,
type: "pleroma:emoji_reaction"
)
assert Impl.perform(notif) == {:ok, [:ok]}
end
@tag capture_log: true
test "returns error if notif does not match " do
assert Impl.perform(%{}) == {:error, :unknown_type}

View File

@ -28,6 +28,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|> put("/api/pleroma/notification_settings", %{
"block_from_strangers" => true,
"bar" => 1
"followers" => false,
"bar" => 1,
"exclude_types" => ["follow"]
})
|> json_response(:ok)
@ -36,6 +39,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
assert %Pleroma.User.NotificationSetting{
block_from_strangers: true,
hide_notification_contents: false
followers: false,
follows: true,
non_follows: true,
non_followers: true,
privacy_option: false,
exclude_types: ["follow"]
} == user.notification_settings
end