forked from AkkomaGang/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
1504211101
21 changed files with 421 additions and 193 deletions
|
@ -13,6 +13,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
||||||
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
||||||
- Mix task to create trusted OAuth App.
|
- Mix task to create trusted OAuth App.
|
||||||
|
- Notifications: Added `follow_request` notification type (configurable, see `[:notifications, :enable_follow_request_notifications]` setting).
|
||||||
|
- Added `:reject_deletes` group to SimplePolicy
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
||||||
|
@ -22,13 +24,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Support pagination in conversations API
|
- Support pagination in conversations API
|
||||||
|
- **Breaking**: SimplePolicy `:reject` and `:accept` allow deletions again
|
||||||
|
|
||||||
## [unreleased-patch]
|
## [unreleased-patch]
|
||||||
### Fixed
|
### Fixed
|
||||||
- Logger configuration through AdminFE
|
- Logger configuration through AdminFE
|
||||||
- HTTP Basic Authentication permissions issue
|
- HTTP Basic Authentication permissions issue
|
||||||
|
- ObjectAgePolicy didn't filter out old messages
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- NodeInfo: ObjectAgePolicy settings to the `federation` list.
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
- Admin API: `GET /api/pleroma/admin/need_reboot`.
|
- Admin API: `GET /api/pleroma/admin/need_reboot`.
|
||||||
|
|
|
@ -337,7 +337,8 @@
|
||||||
reject: [],
|
reject: [],
|
||||||
accept: [],
|
accept: [],
|
||||||
avatar_removal: [],
|
avatar_removal: [],
|
||||||
banner_removal: []
|
banner_removal: [],
|
||||||
|
reject_deletes: []
|
||||||
|
|
||||||
config :pleroma, :mrf_keyword,
|
config :pleroma, :mrf_keyword,
|
||||||
reject: [],
|
reject: [],
|
||||||
|
@ -562,6 +563,8 @@
|
||||||
inactivity_threshold: 7
|
inactivity_threshold: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config :pleroma, :notifications, enable_follow_request_notifications: false
|
||||||
|
|
||||||
config :pleroma, :oauth2,
|
config :pleroma, :oauth2,
|
||||||
token_expires_in: 600,
|
token_expires_in: 600,
|
||||||
issue_new_refresh_token: true,
|
issue_new_refresh_token: true,
|
||||||
|
|
|
@ -1317,13 +1317,13 @@
|
||||||
%{
|
%{
|
||||||
key: :reject,
|
key: :reject,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to reject any activities from",
|
description: "List of instances to reject activities from (except deletes)",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :accept,
|
key: :accept,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to accept any activities from",
|
description: "List of instances to only accept activities from (except deletes)",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
@ -1343,6 +1343,12 @@
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of instances to strip banners from",
|
description: "List of instances to strip banners from",
|
||||||
suggestions: ["example.com", "*.example.com"]
|
suggestions: ["example.com", "*.example.com"]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :reject_deletes,
|
||||||
|
type: {:list, :string},
|
||||||
|
description: "List of instances to reject deletions from",
|
||||||
|
suggestions: ["example.com", "*.example.com"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2267,6 +2273,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: :notifications,
|
||||||
|
type: :group,
|
||||||
|
description: "Notification settings",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enable_follow_request_notifications,
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Enables notifications on new follow requests (causes issues with older PleromaFE versions)."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: Pleroma.Emails.UserEmail,
|
key: Pleroma.Emails.UserEmail,
|
||||||
|
|
|
@ -41,11 +41,15 @@ config :pleroma, :instance,
|
||||||
|
|
||||||
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||||
|
|
||||||
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
|
||||||
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
|
||||||
* `reject`: Servers in this group will have their messages rejected.
|
* `reject`: Servers in this group will have their messages rejected.
|
||||||
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
* `accept`: If not empty, only messages from these instances will be accepted (whitelist federation).
|
||||||
|
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
||||||
|
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
||||||
|
* `avatar_removal`: Avatars from these servers will be stripped from incoming messages.
|
||||||
|
* `banner_removal`: Banner images from these servers will be stripped from incoming messages.
|
||||||
* `report_removal`: Servers in this group will have their reports (flags) rejected.
|
* `report_removal`: Servers in this group will have their reports (flags) rejected.
|
||||||
|
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
||||||
|
* `reject_deletes`: Deletion requests will be rejected from these servers.
|
||||||
|
|
||||||
Servers should be configured as lists.
|
Servers should be configured as lists.
|
||||||
|
|
||||||
|
|
|
@ -27,17 +27,13 @@ defmodule Pleroma.Activity do
|
||||||
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
||||||
@mastodon_notification_types %{
|
@mastodon_notification_types %{
|
||||||
"Create" => "mention",
|
"Create" => "mention",
|
||||||
"Follow" => "follow",
|
"Follow" => ["follow", "follow_request"],
|
||||||
"Announce" => "reblog",
|
"Announce" => "reblog",
|
||||||
"Like" => "favourite",
|
"Like" => "favourite",
|
||||||
"Move" => "move",
|
"Move" => "move",
|
||||||
"EmojiReact" => "pleroma:emoji_reaction"
|
"EmojiReact" => "pleroma:emoji_reaction"
|
||||||
}
|
}
|
||||||
|
|
||||||
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
|
||||||
into: %{},
|
|
||||||
do: {v, k}
|
|
||||||
|
|
||||||
schema "activities" do
|
schema "activities" do
|
||||||
field(:data, :map)
|
field(:data, :map)
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
|
@ -291,15 +287,43 @@ defp purge_web_resp_cache(%Activity{} = activity) do
|
||||||
|
|
||||||
defp purge_web_resp_cache(nil), do: nil
|
defp purge_web_resp_cache(nil), do: nil
|
||||||
|
|
||||||
for {ap_type, type} <- @mastodon_notification_types do
|
def follow_accepted?(
|
||||||
|
%Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity
|
||||||
|
) do
|
||||||
|
with %User{} = follower <- Activity.user_actor(activity),
|
||||||
|
%User{} = followed <- User.get_cached_by_ap_id(followed_ap_id) do
|
||||||
|
Pleroma.FollowingRelationship.following?(follower, followed)
|
||||||
|
else
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_accepted?(_), do: false
|
||||||
|
|
||||||
|
@spec mastodon_notification_type(Activity.t()) :: String.t() | nil
|
||||||
|
|
||||||
|
for {ap_type, type} <- @mastodon_notification_types, not is_list(type) do
|
||||||
def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}),
|
def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}),
|
||||||
do: unquote(type)
|
do: unquote(type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mastodon_notification_type(%Activity{data: %{"type" => "Follow"}} = activity) do
|
||||||
|
if follow_accepted?(activity) do
|
||||||
|
"follow"
|
||||||
|
else
|
||||||
|
"follow_request"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def mastodon_notification_type(%Activity{}), do: nil
|
def mastodon_notification_type(%Activity{}), do: nil
|
||||||
|
|
||||||
|
@spec from_mastodon_notification_type(String.t()) :: String.t() | nil
|
||||||
|
@doc "Converts Mastodon notification type to AR activity type"
|
||||||
def from_mastodon_notification_type(type) do
|
def from_mastodon_notification_type(type) do
|
||||||
Map.get(@mastodon_to_ap_notification_types, type)
|
with {k, _v} <-
|
||||||
|
Enum.find(@mastodon_notification_types, fn {_k, v} -> type in List.wrap(v) end) do
|
||||||
|
k
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def all_by_actor_and_id(actor, status_ids \\ [])
|
def all_by_actor_and_id(actor, status_ids \\ [])
|
||||||
|
|
|
@ -284,8 +284,17 @@ def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = act
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_notifications(%Activity{data: %{"type" => "Follow"}} = activity) do
|
||||||
|
if Pleroma.Config.get([:notifications, :enable_follow_request_notifications]) ||
|
||||||
|
Activity.follow_accepted?(activity) do
|
||||||
|
do_create_notifications(activity)
|
||||||
|
else
|
||||||
|
{:ok, []}
|
||||||
|
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", "Move", "EmojiReact"] do
|
||||||
do_create_notifications(activity)
|
do_create_notifications(activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -688,6 +688,8 @@ def needs_update?(%User{local: false} = user) do
|
||||||
def needs_update?(_), do: true
|
def needs_update?(_), do: true
|
||||||
|
|
||||||
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
|
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||||
|
|
||||||
|
# "Locked" (self-locked) users demand explicit authorization of follow requests
|
||||||
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
|
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
|
||||||
follow(follower, followed, :follow_pending)
|
follow(follower, followed, :follow_pending)
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
||||||
@moduledoc "Filter activities depending on their age"
|
@moduledoc "Filter activities depending on their age"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
defp check_date(%{"published" => published} = message) do
|
defp check_date(%{"object" => %{"published" => published}} = message) do
|
||||||
with %DateTime{} = now <- DateTime.utc_now(),
|
with %DateTime{} = now <- DateTime.utc_now(),
|
||||||
{:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published),
|
{:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published),
|
||||||
max_ttl <- Config.get([:mrf_object_age, :threshold]),
|
max_ttl <- Config.get([:mrf_object_age, :threshold]),
|
||||||
|
@ -96,5 +96,11 @@ def filter(%{"type" => "Create", "published" => _} = message) do
|
||||||
def filter(message), do: {:ok, message}
|
def filter(message), do: {:ok, message}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def describe, do: {:ok, %{}}
|
def describe do
|
||||||
|
mrf_object_age =
|
||||||
|
Pleroma.Config.get(:mrf_object_age)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
{:ok, %{mrf_object_age: mrf_object_age}}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -148,6 +148,21 @@ defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image
|
||||||
|
|
||||||
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(%{"type" => "Delete", "actor" => actor} = object) do
|
||||||
|
%{host: actor_host} = URI.parse(actor)
|
||||||
|
|
||||||
|
reject_deletes =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :reject_deletes])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
|
if MRF.subdomain_match?(reject_deletes, actor_host) do
|
||||||
|
{:reject, nil}
|
||||||
|
else
|
||||||
|
{:ok, object}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"actor" => actor} = object) do
|
def filter(%{"actor" => actor} = object) do
|
||||||
actor_info = URI.parse(actor)
|
actor_info = URI.parse(actor)
|
||||||
|
|
|
@ -6,25 +6,22 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||||
@moduledoc "The module represents functions to manage user subscriptions."
|
@moduledoc "The module represents functions to manage user subscriptions."
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||||
|
plug(:restrict_push_enabled)
|
||||||
|
|
||||||
# Creates PushSubscription
|
# Creates PushSubscription
|
||||||
# POST /api/v1/push/subscription
|
# POST /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, _} <- Subscription.delete_if_exists(user, token),
|
||||||
{:ok, _} <- Subscription.delete_if_exists(user, token),
|
|
||||||
{:ok, subscription} <- Subscription.create(user, token, params) do
|
{:ok, subscription} <- Subscription.create(user, token, params) do
|
||||||
view = View.render("push_subscription.json", subscription: subscription)
|
render(conn, "show.json", subscription: subscription)
|
||||||
json(conn, view)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,10 +29,8 @@ def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
# GET /api/v1/push/subscription
|
# GET /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, subscription} <- Subscription.get(user, token) do
|
||||||
{:ok, subscription} <- Subscription.get(user, token) do
|
render(conn, "show.json", subscription: subscription)
|
||||||
view = View.render("push_subscription.json", subscription: subscription)
|
|
||||||
json(conn, view)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,10 +38,8 @@ def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
# PUT /api/v1/push/subscription
|
# PUT /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, subscription} <- Subscription.update(user, token, params) do
|
||||||
{:ok, subscription} <- Subscription.update(user, token, params) do
|
render(conn, "show.json", subscription: subscription)
|
||||||
view = View.render("push_subscription.json", subscription: subscription)
|
|
||||||
json(conn, view)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -54,11 +47,20 @@ def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
# DELETE /api/v1/push/subscription
|
# DELETE /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
|
def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
with true <- Push.enabled(),
|
with {:ok, _response} <- Subscription.delete(user, token),
|
||||||
{:ok, _response} <- Subscription.delete(user, token),
|
|
||||||
do: json(conn, %{})
|
do: json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp restrict_push_enabled(conn, _) do
|
||||||
|
if Push.enabled() do
|
||||||
|
conn
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> render_error(:forbidden, "Web push subscription is disabled on this Pleroma instance")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# fallback action
|
# fallback action
|
||||||
#
|
#
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
|
|
|
@ -117,14 +117,14 @@ def render(
|
||||||
# Note: :skip_relationships option being applied to _account_ rendering (here)
|
# Note: :skip_relationships option being applied to _account_ rendering (here)
|
||||||
put_target(response, activity, reading_user, render_opts)
|
put_target(response, activity, reading_user, render_opts)
|
||||||
|
|
||||||
"follow" ->
|
|
||||||
response
|
|
||||||
|
|
||||||
"pleroma:emoji_reaction" ->
|
"pleroma:emoji_reaction" ->
|
||||||
response
|
response
|
||||||
|> put_status(parent_activity_fn.(), reading_user, render_opts)
|
|> put_status(parent_activity_fn.(), reading_user, render_opts)
|
||||||
|> put_emoji(activity)
|
|> put_emoji(activity)
|
||||||
|
|
||||||
|
type when type in ["follow", "follow_request"] ->
|
||||||
|
response
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
defmodule Pleroma.Web.MastodonAPI.SubscriptionView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
|
|
||||||
def render("push_subscription.json", %{subscription: subscription}) do
|
def render("show.json", %{subscription: subscription}) do
|
||||||
%{
|
%{
|
||||||
id: to_string(subscription.id),
|
id: to_string(subscription.id),
|
||||||
endpoint: subscription.endpoint,
|
endpoint: subscription.endpoint,
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.Web.Push.Impl do
|
||||||
require Logger
|
require Logger
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
defdelegate mastodon_notification_type(activity), to: Activity
|
||||||
|
|
||||||
@types ["Create", "Follow", "Announce", "Like", "Move"]
|
@types ["Create", "Follow", "Announce", "Like", "Move"]
|
||||||
|
|
||||||
@doc "Performs sending notifications for user subscriptions"
|
@doc "Performs sending notifications for user subscriptions"
|
||||||
|
@ -24,32 +26,32 @@ def perform(
|
||||||
%{
|
%{
|
||||||
activity: %{data: %{"type" => activity_type}} = activity,
|
activity: %{data: %{"type" => activity_type}} = activity,
|
||||||
user: %User{id: user_id}
|
user: %User{id: user_id}
|
||||||
} = notif
|
} = notification
|
||||||
)
|
)
|
||||||
when activity_type in @types do
|
when activity_type in @types do
|
||||||
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
|
actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
|
||||||
|
|
||||||
type = Activity.mastodon_notification_type(notif.activity)
|
mastodon_type = mastodon_notification_type(notification.activity)
|
||||||
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||||
avatar_url = User.avatar_url(actor)
|
avatar_url = User.avatar_url(actor)
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
user = User.get_cached_by_id(user_id)
|
user = User.get_cached_by_id(user_id)
|
||||||
direct_conversation_id = Activity.direct_conversation_id(activity, user)
|
direct_conversation_id = Activity.direct_conversation_id(activity, user)
|
||||||
|
|
||||||
for subscription <- fetch_subsriptions(user_id),
|
for subscription <- fetch_subscriptions(user_id),
|
||||||
get_in(subscription.data, ["alerts", type]) do
|
Subscription.enabled?(subscription, mastodon_type) do
|
||||||
%{
|
%{
|
||||||
access_token: subscription.token.token,
|
access_token: subscription.token.token,
|
||||||
notification_id: notif.id,
|
notification_id: notification.id,
|
||||||
notification_type: type,
|
notification_type: mastodon_type,
|
||||||
icon: avatar_url,
|
icon: avatar_url,
|
||||||
preferred_locale: "en",
|
preferred_locale: "en",
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
activity_id: notif.activity.id,
|
activity_id: notification.activity.id,
|
||||||
direct_conversation_id: direct_conversation_id
|
direct_conversation_id: direct_conversation_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> Map.merge(build_content(notif, actor, object))
|
|> Map.merge(build_content(notification, actor, object, mastodon_type))
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
|> push_message(build_sub(subscription), gcm_api_key, subscription)
|
|> push_message(build_sub(subscription), gcm_api_key, subscription)
|
||||||
end
|
end
|
||||||
|
@ -82,7 +84,7 @@ def push_message(body, sub, api_key, subscription) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Gets user subscriptions"
|
@doc "Gets user subscriptions"
|
||||||
def fetch_subsriptions(user_id) do
|
def fetch_subscriptions(user_id) do
|
||||||
Subscription
|
Subscription
|
||||||
|> where(user_id: ^user_id)
|
|> where(user_id: ^user_id)
|
||||||
|> preload(:token)
|
|> preload(:token)
|
||||||
|
@ -99,28 +101,36 @@ def build_sub(subscription) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_content(notification, actor, object, mastodon_type \\ nil)
|
||||||
|
|
||||||
def build_content(
|
def build_content(
|
||||||
%{
|
%{
|
||||||
activity: %{data: %{"directMessage" => true}},
|
activity: %{data: %{"directMessage" => true}},
|
||||||
user: %{notification_settings: %{privacy_option: true}}
|
user: %{notification_settings: %{privacy_option: true}}
|
||||||
},
|
},
|
||||||
actor,
|
actor,
|
||||||
_
|
_object,
|
||||||
|
_mastodon_type
|
||||||
) do
|
) do
|
||||||
%{title: "New Direct Message", body: "@#{actor.nickname}"}
|
%{title: "New Direct Message", body: "@#{actor.nickname}"}
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_content(notif, actor, object) do
|
def build_content(notification, actor, object, mastodon_type) do
|
||||||
|
mastodon_type = mastodon_type || mastodon_notification_type(notification.activity)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
title: format_title(notif),
|
title: format_title(notification, mastodon_type),
|
||||||
body: format_body(notif, actor, object)
|
body: format_body(notification, actor, object, mastodon_type)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_body(activity, actor, object, mastodon_type \\ nil)
|
||||||
|
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => "Create"}}},
|
%{activity: %{data: %{"type" => "Create"}}},
|
||||||
actor,
|
actor,
|
||||||
%{data: %{"content" => content}}
|
%{data: %{"content" => content}},
|
||||||
|
_mastodon_type
|
||||||
) do
|
) do
|
||||||
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
"@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
end
|
end
|
||||||
|
@ -128,33 +138,44 @@ def format_body(
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => "Announce"}}},
|
%{activity: %{data: %{"type" => "Announce"}}},
|
||||||
actor,
|
actor,
|
||||||
%{data: %{"content" => content}}
|
%{data: %{"content" => content}},
|
||||||
|
_mastodon_type
|
||||||
) do
|
) do
|
||||||
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
"@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_body(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => type}}},
|
%{activity: %{data: %{"type" => type}}} = notification,
|
||||||
actor,
|
actor,
|
||||||
_object
|
_object,
|
||||||
|
mastodon_type
|
||||||
)
|
)
|
||||||
when type in ["Follow", "Like"] do
|
when type in ["Follow", "Like"] do
|
||||||
case type do
|
mastodon_type = mastodon_type || mastodon_notification_type(notification.activity)
|
||||||
"Follow" -> "@#{actor.nickname} has followed you"
|
|
||||||
"Like" -> "@#{actor.nickname} has favorited your post"
|
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"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_title(%{activity: %{data: %{"directMessage" => true}}}) do
|
def format_title(activity, mastodon_type \\ nil)
|
||||||
|
|
||||||
|
def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do
|
||||||
"New Direct Message"
|
"New Direct Message"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_title(%{activity: %{data: %{"type" => type}}}) do
|
def format_title(%{activity: activity}, mastodon_type) do
|
||||||
case type do
|
mastodon_type = mastodon_type || mastodon_notification_type(activity)
|
||||||
"Create" -> "New Mention"
|
|
||||||
"Follow" -> "New Follower"
|
case mastodon_type do
|
||||||
"Announce" -> "New Repeat"
|
"mention" -> "New Mention"
|
||||||
"Like" -> "New Favorite"
|
"follow" -> "New Follower"
|
||||||
|
"follow_request" -> "New Follow Request"
|
||||||
|
"reblog" -> "New Repeat"
|
||||||
|
"favourite" -> "New Favorite"
|
||||||
|
type -> "New #{String.capitalize(type || "event")}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,14 @@ defp alerts(%{"data" => %{"alerts" => alerts}}) do
|
||||||
%{"alerts" => alerts}
|
%{"alerts" => alerts}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def enabled?(subscription, "follow_request") do
|
||||||
|
enabled?(subscription, "follow")
|
||||||
|
end
|
||||||
|
|
||||||
|
def enabled?(subscription, alert_type) do
|
||||||
|
get_in(subscription.data, ["alerts", alert_type])
|
||||||
|
end
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
%User{} = user,
|
%User{} = user,
|
||||||
%Token{} = token,
|
%Token{} = token,
|
||||||
|
|
|
@ -16,79 +16,60 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :api do
|
pipeline :authenticate do
|
||||||
plug(:accepts, ["json"])
|
|
||||||
plug(:fetch_session)
|
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(Pleroma.Plugs.OAuthPlug)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
plug(Pleroma.Plugs.UserFetcherPlug)
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
plug(Pleroma.Plugs.AuthenticationPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :after_auth do
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
end
|
||||||
|
|
||||||
|
pipeline :base_api do
|
||||||
|
plug(:accepts, ["json"])
|
||||||
|
plug(:fetch_session)
|
||||||
|
plug(:authenticate)
|
||||||
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :api do
|
||||||
|
plug(:base_api)
|
||||||
|
plug(:after_auth)
|
||||||
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :authenticated_api do
|
pipeline :authenticated_api do
|
||||||
plug(:accepts, ["json"])
|
plug(:base_api)
|
||||||
plug(:fetch_session)
|
|
||||||
plug(Pleroma.Plugs.AuthExpectedPlug)
|
plug(Pleroma.Plugs.AuthExpectedPlug)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :admin_api do
|
pipeline :admin_api do
|
||||||
plug(:accepts, ["json"])
|
plug(:base_api)
|
||||||
plug(:fetch_session)
|
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
|
plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||||
plug(Pleroma.Plugs.UserIsAdminPlug)
|
plug(Pleroma.Plugs.UserIsAdminPlug)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :mastodon_html do
|
pipeline :mastodon_html do
|
||||||
plug(:accepts, ["html"])
|
plug(:browser)
|
||||||
plug(:fetch_session)
|
plug(:authenticate)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :pleroma_html do
|
pipeline :pleroma_html do
|
||||||
plug(:accepts, ["html"])
|
plug(:browser)
|
||||||
plug(:fetch_session)
|
plug(:authenticate)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -515,7 +496,7 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api" do
|
scope "/api" do
|
||||||
pipe_through(:api)
|
pipe_through(:base_api)
|
||||||
|
|
||||||
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
|
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
|
||||||
end
|
end
|
||||||
|
@ -529,10 +510,6 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
|
post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :ap_service_actor do
|
|
||||||
plug(:accepts, ["activity+json", "json"])
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline :ostatus do
|
pipeline :ostatus do
|
||||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||||
plug(Pleroma.Plugs.StaticFEPlug)
|
plug(Pleroma.Plugs.StaticFEPlug)
|
||||||
|
@ -543,8 +520,7 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
scope "/", Pleroma.Web do
|
||||||
pipe_through(:ostatus)
|
pipe_through([:ostatus, :http_signature])
|
||||||
pipe_through(:http_signature)
|
|
||||||
|
|
||||||
get("/objects/:uuid", OStatus.OStatusController, :object)
|
get("/objects/:uuid", OStatus.OStatusController, :object)
|
||||||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||||
|
@ -562,13 +538,6 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Server to Server (S2S) AP interactions
|
|
||||||
pipeline :activitypub do
|
|
||||||
plug(:accepts, ["activity+json", "json"])
|
|
||||||
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
|
||||||
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
|
|
||||||
end
|
|
||||||
|
|
||||||
scope "/", Pleroma.Web.ActivityPub do
|
scope "/", Pleroma.Web.ActivityPub do
|
||||||
# XXX: not really ostatus
|
# XXX: not really ostatus
|
||||||
pipe_through(:ostatus)
|
pipe_through(:ostatus)
|
||||||
|
@ -576,19 +545,22 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
get("/users/:nickname/outbox", ActivityPubController, :outbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :ap_service_actor do
|
||||||
|
plug(:accepts, ["activity+json", "json"])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Server to Server (S2S) AP interactions
|
||||||
|
pipeline :activitypub do
|
||||||
|
plug(:ap_service_actor)
|
||||||
|
plug(:http_signature)
|
||||||
|
end
|
||||||
|
|
||||||
# Client to Server (C2S) AP interactions
|
# Client to Server (C2S) AP interactions
|
||||||
pipeline :activitypub_client do
|
pipeline :activitypub_client do
|
||||||
plug(:accepts, ["activity+json", "json"])
|
plug(:ap_service_actor)
|
||||||
plug(:fetch_session)
|
plug(:fetch_session)
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(:authenticate)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
|
||||||
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.AuthenticationPlug)
|
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
|
||||||
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
|
||||||
plug(Pleroma.Plugs.EnsureUserKeyPlug)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.ActivityPub do
|
scope "/", Pleroma.Web.ActivityPub do
|
||||||
|
@ -660,12 +632,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/web/*path", MastoFEController, :index)
|
get("/web/*path", MastoFEController, :index)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :remote_media do
|
|
||||||
end
|
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web.MediaProxy do
|
scope "/proxy/", Pleroma.Web.MediaProxy do
|
||||||
pipe_through(:remote_media)
|
|
||||||
|
|
||||||
get("/:sig/:url", MediaProxyController, :remote)
|
get("/:sig/:url", MediaProxyController, :remote)
|
||||||
get("/:sig/:url/:filename", MediaProxyController, :remote)
|
get("/:sig/:url/:filename", MediaProxyController, :remote)
|
||||||
end
|
end
|
||||||
|
|
31
mix.exs
31
mix.exs
|
@ -221,19 +221,26 @@ defp version(version) do
|
||||||
identifier_filter = ~r/[^0-9a-z\-]+/i
|
identifier_filter = ~r/[^0-9a-z\-]+/i
|
||||||
|
|
||||||
# Pre-release version, denoted from patch version with a hyphen
|
# Pre-release version, denoted from patch version with a hyphen
|
||||||
|
{tag, tag_err} =
|
||||||
|
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true)
|
||||||
|
|
||||||
|
{describe, describe_err} = System.cmd("git", ["describe", "--tags", "--abbrev=8"])
|
||||||
|
{commit_hash, commit_hash_err} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
||||||
|
|
||||||
git_pre_release =
|
git_pre_release =
|
||||||
with {tag, 0} <-
|
cond do
|
||||||
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true),
|
tag_err == 0 and describe_err == 0 ->
|
||||||
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]) do
|
describe
|
||||||
describe
|
|> String.trim()
|
||||||
|> String.trim()
|
|> String.replace(String.trim(tag), "")
|
||||||
|> String.replace(String.trim(tag), "")
|
|> String.trim_leading("-")
|
||||||
|> String.trim_leading("-")
|
|> String.trim()
|
||||||
|> String.trim()
|
|
||||||
else
|
commit_hash_err == 0 ->
|
||||||
_ ->
|
|
||||||
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
|
||||||
"0-g" <> String.trim(commit_hash)
|
"0-g" <> String.trim(commit_hash)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
""
|
||||||
end
|
end
|
||||||
|
|
||||||
# Branch name as pre-release version component, denoted with a dot
|
# Branch name as pre-release version component, denoted with a dot
|
||||||
|
@ -251,6 +258,8 @@ defp version(version) do
|
||||||
|> String.replace(identifier_filter, "-")
|
|> String.replace(identifier_filter, "-")
|
||||||
|
|
||||||
branch_name
|
branch_name
|
||||||
|
else
|
||||||
|
_ -> "stable"
|
||||||
end
|
end
|
||||||
|
|
||||||
build_name =
|
build_name =
|
||||||
|
|
|
@ -8,11 +8,13 @@ defmodule Pleroma.NotificationTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import Mock
|
import Mock
|
||||||
|
|
||||||
|
alias Pleroma.FollowingRelationship
|
||||||
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.MastodonAPI.NotificationView
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
|
|
||||||
|
@ -272,16 +274,6 @@ test "it doesn't create a notification for user if he is the activity author" do
|
||||||
refute Notification.create_notification(activity, author)
|
refute Notification.create_notification(activity, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
|
||||||
user = insert(:user)
|
|
||||||
followed_user = insert(:user)
|
|
||||||
{:ok, _, _, activity} = CommonAPI.follow(user, followed_user)
|
|
||||||
Notification.create_notification(activity, followed_user)
|
|
||||||
CommonAPI.unfollow(user, followed_user)
|
|
||||||
{:ok, _, _, activity_dupe} = CommonAPI.follow(user, followed_user)
|
|
||||||
refute Notification.create_notification(activity_dupe, followed_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it doesn't create duplicate notifications for follow+subscribed users" do
|
test "it doesn't create duplicate notifications for follow+subscribed users" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
subscriber = insert(:user)
|
subscriber = insert(:user)
|
||||||
|
@ -304,6 +296,74 @@ test "it doesn't create subscription notifications if the recipient cannot see t
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "follow / follow_request notifications" do
|
||||||
|
test "it creates `follow` notification for approved Follow activity" do
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: false)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
assert FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [notification] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
assert %{type: "follow"} =
|
||||||
|
NotificationView.render("show.json", %{
|
||||||
|
notification: notification,
|
||||||
|
for: followed_user
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if `follow_request` notifications are enabled, " <>
|
||||||
|
"it creates `follow_request` notification for pending Follow activity" do
|
||||||
|
clear_config([:notifications, :enable_follow_request_notifications], true)
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: true)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
refute FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [notification] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
render_opts = %{notification: notification, for: followed_user}
|
||||||
|
assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
|
||||||
|
|
||||||
|
# After request is accepted, the same notification is rendered with type "follow":
|
||||||
|
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
|
||||||
|
|
||||||
|
notification_id = notification.id
|
||||||
|
assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
|
||||||
|
assert %{type: "follow"} = NotificationView.render("show.json", render_opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if `follow_request` notifications are disabled, " <>
|
||||||
|
"it does NOT create `follow*` notification for pending Follow activity" do
|
||||||
|
clear_config([:notifications, :enable_follow_request_notifications], false)
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: true)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
refute FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
# After request is accepted, no new notifications are generated:
|
||||||
|
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
|
||||||
|
assert [] = Notification.for_user(followed_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
||||||
|
user = insert(:user)
|
||||||
|
followed_user = insert(:user, locked: false)
|
||||||
|
|
||||||
|
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||||
|
assert FollowingRelationship.following?(user, followed_user)
|
||||||
|
assert [notification] = Notification.for_user(followed_user)
|
||||||
|
|
||||||
|
CommonAPI.unfollow(user, followed_user)
|
||||||
|
{:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
|
||||||
|
|
||||||
|
notification_id = notification.id
|
||||||
|
assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "get notification" do
|
describe "get notification" do
|
||||||
test "it gets a notification that belongs to the user" do
|
test "it gets a notification that belongs to the user" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -20,26 +20,38 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_old_message do
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_new_message do
|
||||||
|
old_message = get_old_message()
|
||||||
|
|
||||||
|
new_object =
|
||||||
|
old_message
|
||||||
|
|> Map.get("object")
|
||||||
|
|> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601())
|
||||||
|
|
||||||
|
old_message
|
||||||
|
|> Map.put("object", new_object)
|
||||||
|
end
|
||||||
|
|
||||||
describe "with reject action" do
|
describe "with reject action" do
|
||||||
test "it rejects an old post" do
|
test "it rejects an old post" do
|
||||||
Config.put([:mrf_object_age, :actions], [:reject])
|
Config.put([:mrf_object_age, :actions], [:reject])
|
||||||
|
|
||||||
data =
|
data = get_old_message()
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|
|
||||||
{:reject, _} = ObjectAgePolicy.filter(data)
|
assert match?({:reject, _}, ObjectAgePolicy.filter(data))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it allows a new post" do
|
test "it allows a new post" do
|
||||||
Config.put([:mrf_object_age, :actions], [:reject])
|
Config.put([:mrf_object_age, :actions], [:reject])
|
||||||
|
|
||||||
data =
|
data = get_new_message()
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601())
|
|
||||||
|
|
||||||
{:ok, _} = ObjectAgePolicy.filter(data)
|
assert match?({:ok, _}, ObjectAgePolicy.filter(data))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -47,9 +59,7 @@ test "it allows a new post" do
|
||||||
test "it delists an old post" do
|
test "it delists an old post" do
|
||||||
Config.put([:mrf_object_age, :actions], [:delist])
|
Config.put([:mrf_object_age, :actions], [:delist])
|
||||||
|
|
||||||
data =
|
data = get_old_message()
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
|
{:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
|
||||||
|
|
||||||
|
@ -61,14 +71,11 @@ test "it delists an old post" do
|
||||||
test "it allows a new post" do
|
test "it allows a new post" do
|
||||||
Config.put([:mrf_object_age, :actions], [:delist])
|
Config.put([:mrf_object_age, :actions], [:delist])
|
||||||
|
|
||||||
data =
|
data = get_new_message()
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601())
|
|
||||||
|
|
||||||
{:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"])
|
{:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"])
|
||||||
|
|
||||||
{:ok, ^data} = ObjectAgePolicy.filter(data)
|
assert match?({:ok, ^data}, ObjectAgePolicy.filter(data))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -76,9 +83,7 @@ test "it allows a new post" do
|
||||||
test "it strips followers collections from an old post" do
|
test "it strips followers collections from an old post" do
|
||||||
Config.put([:mrf_object_age, :actions], [:strip_followers])
|
Config.put([:mrf_object_age, :actions], [:strip_followers])
|
||||||
|
|
||||||
data =
|
data = get_old_message()
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, user} = User.get_or_fetch_by_ap_id(data["actor"])
|
{:ok, user} = User.get_or_fetch_by_ap_id(data["actor"])
|
||||||
|
|
||||||
|
@ -91,14 +96,11 @@ test "it strips followers collections from an old post" do
|
||||||
test "it allows a new post" do
|
test "it allows a new post" do
|
||||||
Config.put([:mrf_object_age, :actions], [:strip_followers])
|
Config.put([:mrf_object_age, :actions], [:strip_followers])
|
||||||
|
|
||||||
data =
|
data = get_new_message()
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601())
|
|
||||||
|
|
||||||
{:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
|
{:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
|
||||||
|
|
||||||
{:ok, ^data} = ObjectAgePolicy.filter(data)
|
assert match?({:ok, ^data}, ObjectAgePolicy.filter(data))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
||||||
reject: [],
|
reject: [],
|
||||||
accept: [],
|
accept: [],
|
||||||
avatar_removal: [],
|
avatar_removal: [],
|
||||||
banner_removal: []
|
banner_removal: [],
|
||||||
|
reject_deletes: []
|
||||||
)
|
)
|
||||||
|
|
||||||
describe "when :media_removal" do
|
describe "when :media_removal" do
|
||||||
|
@ -382,6 +383,66 @@ test "match with wildcard domain" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "when :reject_deletes is empty" do
|
||||||
|
setup do: Config.put([:mrf_simple, :reject_deletes], [])
|
||||||
|
|
||||||
|
test "it accepts deletions even from rejected servers" do
|
||||||
|
Config.put([:mrf_simple, :reject], ["remote.instance"])
|
||||||
|
|
||||||
|
deletion_message = build_remote_deletion_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts deletions even from non-whitelisted servers" do
|
||||||
|
Config.put([:mrf_simple, :accept], ["non.matching.remote"])
|
||||||
|
|
||||||
|
deletion_message = build_remote_deletion_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when :reject_deletes is not empty but it doesn't have a matching host" do
|
||||||
|
setup do: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"])
|
||||||
|
|
||||||
|
test "it accepts deletions even from rejected servers" do
|
||||||
|
Config.put([:mrf_simple, :reject], ["remote.instance"])
|
||||||
|
|
||||||
|
deletion_message = build_remote_deletion_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts deletions even from non-whitelisted servers" do
|
||||||
|
Config.put([:mrf_simple, :accept], ["non.matching.remote"])
|
||||||
|
|
||||||
|
deletion_message = build_remote_deletion_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when :reject_deletes has a matching host" do
|
||||||
|
setup do: Config.put([:mrf_simple, :reject_deletes], ["remote.instance"])
|
||||||
|
|
||||||
|
test "it rejects the deletion" do
|
||||||
|
deletion_message = build_remote_deletion_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(deletion_message) == {:reject, nil}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when :reject_deletes match with wildcard domain" do
|
||||||
|
setup do: Config.put([:mrf_simple, :reject_deletes], ["*.remote.instance"])
|
||||||
|
|
||||||
|
test "it rejects the deletion" do
|
||||||
|
deletion_message = build_remote_deletion_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(deletion_message) == {:reject, nil}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp build_local_message do
|
defp build_local_message do
|
||||||
%{
|
%{
|
||||||
"actor" => "#{Pleroma.Web.base_url()}/users/alice",
|
"actor" => "#{Pleroma.Web.base_url()}/users/alice",
|
||||||
|
@ -408,4 +469,11 @@ defp build_remote_user do
|
||||||
"type" => "Person"
|
"type" => "Person"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp build_remote_deletion_message do
|
||||||
|
%{
|
||||||
|
"type" => "Delete",
|
||||||
|
"actor" => "https://remote.instance/users/bob"
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,7 +35,10 @@ defmacro assert_error_when_disable_push(do: yield) do
|
||||||
quote do
|
quote do
|
||||||
vapid_details = Application.get_env(:web_push_encryption, :vapid_details, [])
|
vapid_details = Application.get_env(:web_push_encryption, :vapid_details, [])
|
||||||
Application.put_env(:web_push_encryption, :vapid_details, [])
|
Application.put_env(:web_push_encryption, :vapid_details, [])
|
||||||
assert "Something went wrong" == unquote(yield)
|
|
||||||
|
assert %{"error" => "Web push subscription is disabled on this Pleroma instance"} ==
|
||||||
|
unquote(yield)
|
||||||
|
|
||||||
Application.put_env(:web_push_encryption, :vapid_details, vapid_details)
|
Application.put_env(:web_push_encryption, :vapid_details, vapid_details)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -45,7 +48,7 @@ test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> post("/api/v1/push/subscription", %{})
|
|> post("/api/v1/push/subscription", %{})
|
||||||
|> json_response(500)
|
|> json_response(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/push/subscription", %{})
|
|> get("/api/v1/push/subscription", %{})
|
||||||
|> json_response(500)
|
|> json_response(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -127,7 +130,7 @@ test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}})
|
|> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}})
|
||||||
|> json_response(500)
|
|> json_response(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -155,7 +158,7 @@ test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> delete("/api/v1/push/subscription", %{})
|
|> delete("/api/v1/push/subscription", %{})
|
||||||
|> json_response(500)
|
|> json_response(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionViewTest do
|
defmodule Pleroma.Web.MastodonAPI.SubscriptionViewTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
|
alias Pleroma.Web.MastodonAPI.SubscriptionView, as: View
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
|
|
||||||
test "Represent a subscription" do
|
test "Represent a subscription" do
|
||||||
|
@ -18,6 +18,6 @@ test "Represent a subscription" do
|
||||||
server_key: Keyword.get(Push.vapid_config(), :public_key)
|
server_key: Keyword.get(Push.vapid_config(), :public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == View.render("push_subscription.json", %{subscription: subscription})
|
assert expected == View.render("show.json", %{subscription: subscription})
|
||||||
end
|
end
|
||||||
end
|
end
|
Loading…
Reference in a new issue