diff options
author | Maksim <parallel588@gmail.com> | 2019-03-06 13:20:12 +0000 |
---|---|---|
committer | kaniini <nenolod@gmail.com> | 2019-03-06 13:20:12 +0000 |
commit | bc7570c282ea45d2d0d4b44c4a07b5d0bda2fea8 (patch) | |
tree | 949a440fd0cbeda1a37ecd3022d891345322de64 /lib/pleroma/web/push | |
parent | 4cbaab2181e7dd5e471b78481997fa4ad049e192 (diff) | |
download | pleroma-bc7570c282ea45d2d0d4b44c4a07b5d0bda2fea8.tar.gz |
[#647] tests for web push
Diffstat (limited to 'lib/pleroma/web/push')
-rw-r--r-- | lib/pleroma/web/push/impl.ex | 127 | ||||
-rw-r--r-- | lib/pleroma/web/push/push.ex | 135 | ||||
-rw-r--r-- | lib/pleroma/web/push/subscription.ex | 24 |
3 files changed, 163 insertions, 123 deletions
diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex new file mode 100644 index 000000000..33f912d34 --- /dev/null +++ b/lib/pleroma/web/push/impl.ex @@ -0,0 +1,127 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Push.Impl do + @moduledoc "The module represents implementation push web notification" + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.Push.Subscription + alias Pleroma.Web.Metadata.Utils + alias Pleroma.Notification + + require Logger + import Ecto.Query + + @types ["Create", "Follow", "Announce", "Like"] + + @doc "Performs sending notifications for user subscriptions" + @spec perform_send(Notification.t()) :: list(any) + def perform_send(%{activity: %{data: %{"type" => activity_type}}, user_id: user_id} = notif) + when activity_type in @types do + actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + + type = Activity.mastodon_notification_type(notif.activity) + gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) + avatar_url = User.avatar_url(actor) + + for subscription <- fetch_subsriptions(user_id), + get_in(subscription.data, ["alerts", type]) do + %{ + title: format_title(notif), + access_token: subscription.token.token, + body: format_body(notif, actor), + notification_id: notif.id, + notification_type: type, + icon: avatar_url, + preferred_locale: "en" + } + |> Jason.encode!() + |> push_message(build_sub(subscription), gcm_api_key, subscription) + end + end + + def perform_send(_) do + Logger.warn("Unknown notification type") + :error + end + + @doc "Push message to web" + def push_message(body, sub, api_key, subscription) do + case WebPushEncryption.send_web_push(body, sub, api_key) do + {:ok, %{status_code: code}} when 400 <= code and code < 500 -> + Logger.debug("Removing subscription record") + Repo.delete!(subscription) + :ok + + {:ok, %{status_code: code}} when 200 <= code and code < 300 -> + :ok + + {:ok, %{status_code: code}} -> + Logger.error("Web Push Notification failed with code: #{code}") + :error + + _ -> + Logger.error("Web Push Notification failed with unknown error") + :error + end + end + + @doc "Gets user subscriptions" + def fetch_subsriptions(user_id) do + Subscription + |> where(user_id: ^user_id) + |> preload(:token) + |> Repo.all() + end + + def build_sub(subscription) do + %{ + keys: %{ + p256dh: subscription.key_p256dh, + auth: subscription.key_auth + }, + endpoint: subscription.endpoint + } + end + + def format_body( + %{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}}, + actor + ) do + "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" + end + + def format_body( + %{activity: %{data: %{"type" => "Announce", "object" => activity_id}}}, + actor + ) do + %Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id) + %Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id) + + "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" + end + + def format_body( + %{activity: %{data: %{"type" => type}}}, + actor + ) + when type in ["Follow", "Like"] do + case type do + "Follow" -> "@#{actor.nickname} has followed you" + "Like" -> "@#{actor.nickname} has favorited your post" + end + end + + def format_title(%{activity: %{data: %{"type" => type}}}) do + case type do + "Create" -> "New Mention" + "Follow" -> "New Follower" + "Announce" -> "New Repeat" + "Like" -> "New Favorite" + end + end +end diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push/push.ex index 92f8f9ab4..951dab535 100644 --- a/lib/pleroma/web/push/push.ex +++ b/lib/pleroma/web/push/push.ex @@ -5,17 +5,13 @@ defmodule Pleroma.Web.Push do use GenServer - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.Push.Subscription - alias Pleroma.Web.Metadata.Utils + alias Pleroma.Web.Push.Impl require Logger - import Ecto.Query - @types ["Create", "Follow", "Announce", "Like"] + ############## + # Client API # + ############## def start_link() do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) @@ -33,14 +29,18 @@ defmodule Pleroma.Web.Push do end end - def send(notification) do - if enabled() do - GenServer.cast(Pleroma.Web.Push, {:send, notification}) - end - end + def send(notification), + do: GenServer.cast(__MODULE__, {:send, notification}) + + #################### + # Server Callbacks # + #################### + @impl true def init(:ok) do - if !enabled() do + if enabled() do + {:ok, nil} + else Logger.warn(""" VAPID key pair is not found. If you wish to enabled web push, please run @@ -50,112 +50,15 @@ defmodule Pleroma.Web.Push do """) :ignore - else - {:ok, nil} end end - def handle_cast( - {:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification}, - state - ) - when type in @types do - actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) - - type = Pleroma.Activity.mastodon_notification_type(notification.activity) - - Subscription - |> where(user_id: ^user_id) - |> preload(:token) - |> Repo.all() - |> Enum.filter(fn subscription -> - get_in(subscription.data, ["alerts", type]) || false - end) - |> Enum.each(fn subscription -> - sub = %{ - keys: %{ - p256dh: subscription.key_p256dh, - auth: subscription.key_auth - }, - endpoint: subscription.endpoint - } - - body = - Jason.encode!(%{ - title: format_title(notification), - access_token: subscription.token.token, - body: format_body(notification, actor), - notification_id: notification.id, - notification_type: type, - icon: User.avatar_url(actor), - preferred_locale: "en" - }) - - case WebPushEncryption.send_web_push( - body, - sub, - Application.get_env(:web_push_encryption, :gcm_api_key) - ) do - {:ok, %{status_code: code}} when 400 <= code and code < 500 -> - Logger.debug("Removing subscription record") - Repo.delete!(subscription) - :ok - - {:ok, %{status_code: code}} when 200 <= code and code < 300 -> - :ok - - {:ok, %{status_code: code}} -> - Logger.error("Web Push Notification failed with code: #{code}") - :error - - _ -> - Logger.error("Web Push Notification failed with unknown error") - :error - end - end) - - {:noreply, state} - end - - def handle_cast({:send, _}, state) do - Logger.warn("Unknown notification type") - {:noreply, state} - end - - def format_body( - %{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}}, - actor - ) do - "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" - end - - def format_body( - %{activity: %{data: %{"type" => "Announce", "object" => activity_id}}}, - actor - ) do - %Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id) - %Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id) - - "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" - end - - def format_body( - %{activity: %{data: %{"type" => type}}}, - actor - ) - when type in ["Follow", "Like"] do - case type do - "Follow" -> "@#{actor.nickname} has followed you" - "Like" -> "@#{actor.nickname} has favorited your post" + @impl true + def handle_cast({:send, notification}, state) do + if enabled() do + Impl.perform_send(notification) end - end - defp format_title(%{activity: %{data: %{"type" => type}}}) do - case type do - "Create" -> "New Mention" - "Follow" -> "New Follower" - "Announce" -> "New Repeat" - "Like" -> "New Favorite" - end + {:noreply, state} end end diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 242e30910..c90bd2bda 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Web.Push.Subscription do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.Push.Subscription + @type t :: %__MODULE__{} + schema "push_subscriptions" do belongs_to(:user, User, type: Pleroma.FlakeId) belongs_to(:token, Token) @@ -50,24 +52,32 @@ defmodule Pleroma.Web.Push.Subscription do }) end + @doc "Gets subsciption by user & token" + @spec get(User.t(), Token.t()) :: {:ok, t()} | {:error, :not_found} def get(%User{id: user_id}, %Token{id: token_id}) do - Repo.get_by(Subscription, user_id: user_id, token_id: token_id) + case Repo.get_by(Subscription, user_id: user_id, token_id: token_id) do + nil -> {:error, :not_found} + subscription -> {:ok, subscription} + end end def update(user, token, params) do - get(user, token) - |> change(data: alerts(params)) - |> Repo.update() + with {:ok, subscription} <- get(user, token) do + subscription + |> change(data: alerts(params)) + |> Repo.update() + end end def delete(user, token) do - Repo.delete(get(user, token)) + with {:ok, subscription} <- get(user, token), + do: Repo.delete(subscription) end def delete_if_exists(user, token) do case get(user, token) do - nil -> {:ok, nil} - sub -> Repo.delete(sub) + {:error, _} -> {:ok, nil} + {:ok, sub} -> Repo.delete(sub) end end |