Compare commits

...

9 Commits

Author SHA1 Message Date
Alexander Strizhakov
965f88cfc7
formatting 2021-03-10 16:46:58 +03:00
Alexander Strizhakov
f1781c14eb
fixes after rebase 2021-03-10 16:46:58 +03:00
Alexander Strizhakov
af6725121f
removing saving settings in mastofe 2021-03-10 16:46:57 +03:00
Alexander Strizhakov
5361e40162
updated difference docs 2021-03-10 16:46:57 +03:00
Alexander Strizhakov
f6ae86a081
some clean up 2021-03-10 16:46:56 +03:00
Alexander Strizhakov
9f33c5fe63
Apply suggestion to lib/pleroma/web/masto_fe_controller.ex 2021-03-10 16:46:56 +03:00
Alexander Strizhakov
46492a2aee
set alert types in compile time 2021-03-10 16:46:55 +03:00
Alexander Strizhakov
a74d0f8b27
little test change 2021-03-10 16:46:55 +03:00
Alexander Strizhakov
16bcffd64a
unify notification settings 2021-03-10 16:46:52 +03:00
14 changed files with 199 additions and 39 deletions

View File

@ -334,6 +334,10 @@ 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
Subscription `alerts` field contains new boolean setting for emoji reactions - `pleroma:emoji_reaction`. Can be passed with other `alerts` settings in POST/PUT `/api/v1/push/subscription` endpoints.
## 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

@ -5,6 +5,9 @@
defmodule Pleroma.Notification do
use Ecto.Schema
import Ecto.Query
import Ecto.Changeset
alias Ecto.Multi
alias Pleroma.Activity
alias Pleroma.FollowingRelationship
@ -20,15 +23,26 @@ defmodule Pleroma.Notification do
alias Pleroma.Web.Push
alias Pleroma.Web.Streamer
import Ecto.Query
import Ecto.Changeset
require Logger
@type t :: %__MODULE__{}
@include_muted_option :with_muted
@types ~w{
favourite
follow
follow_request
mention
move
pleroma:chat_mention
pleroma:emoji_reaction
pleroma:report
reblog
}
@types_excluding_chat_and_report @types -- ~w(pleroma:chat_mention pleroma:report)
@type t :: %__MODULE__{}
schema "notifications" do
field(:seen, :boolean, default: false)
# This is an enum type in the database. If you add a new notification type,
@ -62,22 +76,16 @@ defmodule Pleroma.Notification do
|> Repo.aggregate(:count, :id)
end
@notification_types ~w{
favourite
follow
follow_request
mention
move
pleroma:chat_mention
pleroma:emoji_reaction
pleroma:report
reblog
}
@spec types() :: [String.t()]
def types, do: @types
@spec types_excluding_chat_and_report() :: [String.t()]
def types_excluding_chat_and_report, do: @types_excluding_chat_and_report
def changeset(%Notification{} = notification, attrs) do
notification
|> cast(attrs, [:seen, :type])
|> validate_inclusion(:type, @notification_types)
|> validate_inclusion(:type, @types)
end
@spec last_read_query(User.t()) :: Ecto.Queryable.t()

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

@ -42,19 +42,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
end
end
@default_notification_types ~w{
mention
follow
follow_request
reblog
favourite
move
pleroma:emoji_reaction
}
def index(%{assigns: %{user: user}} = conn, params) do
params =
Map.new(params, fn {k, v} -> {to_string(k), v} end)
|> Map.put_new("include_types", @default_notification_types)
params
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|> Map.put_new("include_types", Pleroma.Notification.types_excluding_chat_and_report())
notifications = MastodonAPI.get_notifications(user, params)

View File

@ -50,7 +50,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
end
def get_notifications(user, params \\ %{}) do
options = cast_params(params)
options =
params
|> Map.put_new("exclude_types", user.notification_settings.exclude_types)
|> cast_params()
user
|> Notification.for_user_query(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

@ -14,6 +14,8 @@ defmodule Pleroma.Web.Push.Subscription do
@type t :: %__MODULE__{}
@supported_alert_types Enum.map(Pleroma.Notification.types(), &String.to_atom/1)
schema "push_subscriptions" do
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
belongs_to(:token, Token)

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

@ -124,7 +124,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
notification_settings = %{
block_from_strangers: false,
hide_notification_contents: 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

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