logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

impl.ex (6035B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.Push.Impl do
  5. @moduledoc "The module represents implementation push web notification"
  6. alias Pleroma.Activity
  7. alias Pleroma.Notification
  8. alias Pleroma.Object
  9. alias Pleroma.Repo
  10. alias Pleroma.User
  11. alias Pleroma.Web.Metadata.Utils
  12. alias Pleroma.Web.Push.Subscription
  13. require Logger
  14. import Ecto.Query
  15. @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact", "Update"]
  16. @doc "Performs sending notifications for user subscriptions"
  17. @spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type}
  18. def perform(
  19. %{
  20. activity: %{data: %{"type" => activity_type}} = activity,
  21. user: %User{id: user_id}
  22. } = notification
  23. )
  24. when activity_type in @types do
  25. actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
  26. mastodon_type = notification.type
  27. gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
  28. avatar_url = User.avatar_url(actor)
  29. object = Object.normalize(activity, fetch: false)
  30. user = User.get_cached_by_id(user_id)
  31. direct_conversation_id = Activity.direct_conversation_id(activity, user)
  32. for subscription <- fetch_subscriptions(user_id),
  33. Subscription.enabled?(subscription, mastodon_type) do
  34. %{
  35. access_token: subscription.token.token,
  36. notification_id: notification.id,
  37. notification_type: mastodon_type,
  38. icon: avatar_url,
  39. preferred_locale: "en",
  40. pleroma: %{
  41. activity_id: notification.activity.id,
  42. direct_conversation_id: direct_conversation_id
  43. }
  44. }
  45. |> Map.merge(build_content(notification, actor, object, mastodon_type))
  46. |> Jason.encode!()
  47. |> push_message(build_sub(subscription), gcm_api_key, subscription)
  48. end
  49. |> (&{:ok, &1}).()
  50. end
  51. def perform(_) do
  52. Logger.warning("Unknown notification type")
  53. {:error, :unknown_type}
  54. end
  55. @doc "Push message to web"
  56. def push_message(body, sub, api_key, subscription) do
  57. case WebPushEncryption.send_web_push(body, sub, api_key) do
  58. {:ok, %{status: code}} when code in 400..499 ->
  59. Logger.debug("Removing subscription record")
  60. Repo.delete!(subscription)
  61. :ok
  62. {:ok, %{status: code}} when code in 200..299 ->
  63. :ok
  64. {:ok, %{status: code}} ->
  65. Logger.error("Web Push Notification failed with code: #{code}")
  66. :error
  67. error ->
  68. Logger.error("Web Push Notification failed with #{inspect(error)}")
  69. :error
  70. end
  71. end
  72. @doc "Gets user subscriptions"
  73. def fetch_subscriptions(user_id) do
  74. Subscription
  75. |> where(user_id: ^user_id)
  76. |> preload(:token)
  77. |> Repo.all()
  78. end
  79. def build_sub(subscription) do
  80. %{
  81. keys: %{
  82. p256dh: subscription.key_p256dh,
  83. auth: subscription.key_auth
  84. },
  85. endpoint: subscription.endpoint
  86. }
  87. end
  88. def build_content(notification, actor, object, mastodon_type \\ nil)
  89. def build_content(
  90. %{
  91. user: %{notification_settings: %{hide_notification_contents: true}}
  92. } = notification,
  93. _actor,
  94. _object,
  95. mastodon_type
  96. ) do
  97. %{body: format_title(notification, mastodon_type)}
  98. end
  99. def build_content(notification, actor, object, mastodon_type) do
  100. mastodon_type = mastodon_type || notification.type
  101. %{
  102. title: format_title(notification, mastodon_type),
  103. body: format_body(notification, actor, object, mastodon_type)
  104. }
  105. end
  106. def format_body(activity, actor, object, mastodon_type \\ nil)
  107. def format_body(_activity, actor, %{data: %{"type" => "ChatMessage"} = data}, _) do
  108. case data["content"] do
  109. nil -> "@#{actor.nickname}: (Attachment)"
  110. content -> "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
  111. end
  112. end
  113. def format_body(
  114. %{activity: %{data: %{"type" => "Create"}}},
  115. actor,
  116. %{data: %{"content" => content}},
  117. _mastodon_type
  118. ) do
  119. "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
  120. end
  121. def format_body(
  122. %{activity: %{data: %{"type" => "Announce"}}},
  123. actor,
  124. %{data: %{"content" => content}},
  125. _mastodon_type
  126. ) do
  127. "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
  128. end
  129. def format_body(
  130. %{activity: %{data: %{"type" => "EmojiReact", "content" => content}}},
  131. actor,
  132. _object,
  133. _mastodon_type
  134. ) do
  135. "@#{actor.nickname} reacted with #{content}"
  136. end
  137. def format_body(
  138. %{activity: %{data: %{"type" => type}}} = notification,
  139. actor,
  140. _object,
  141. mastodon_type
  142. )
  143. when type in ["Follow", "Like"] do
  144. mastodon_type = mastodon_type || notification.type
  145. case mastodon_type do
  146. "follow" -> "@#{actor.nickname} has followed you"
  147. "follow_request" -> "@#{actor.nickname} has requested to follow you"
  148. "favourite" -> "@#{actor.nickname} has favorited your post"
  149. end
  150. end
  151. def format_body(
  152. %{activity: %{data: %{"type" => "Update"}}},
  153. actor,
  154. _object,
  155. _mastodon_type
  156. ) do
  157. "@#{actor.nickname} edited a status"
  158. end
  159. def format_title(activity, mastodon_type \\ nil)
  160. def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do
  161. "New Direct Message"
  162. end
  163. def format_title(%{type: type}, mastodon_type) do
  164. case mastodon_type || type do
  165. "mention" -> "New Mention"
  166. "follow" -> "New Follower"
  167. "follow_request" -> "New Follow Request"
  168. "reblog" -> "New Repeat"
  169. "favourite" -> "New Favorite"
  170. "update" -> "New Update"
  171. "pleroma:chat_mention" -> "New Chat Message"
  172. "pleroma:emoji_reaction" -> "New Reaction"
  173. type -> "New #{String.capitalize(type || "event")}"
  174. end
  175. end
  176. end