aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHaelwenn <contact+git.pleroma.social@hacktivis.me>2021-08-09 10:02:37 +0000
committerHaelwenn <contact+git.pleroma.social@hacktivis.me>2021-08-09 10:02:37 +0000
commit901204df2292419378a486a97fea9d3c70903a20 (patch)
treee7b2b68201819dce06079d4decfacaa4c8aa1043 /lib
parent6384d7803520f633a66d1cb9f76e3540863f92fb (diff)
parent85d71d4f1d433a168eb136ad88b651b6c1d1a4fc (diff)
downloadpleroma-901204df2292419378a486a97fea9d3c70903a20.tar.gz
Merge branch 'poll-notification' into 'develop'
MastodonAPI: Support poll notification See merge request pleroma/pleroma!3484
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/notification.ex81
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex7
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex20
-rw-r--r--lib/pleroma/web/api_spec/operations/notification_operation.ex3
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/notification_controller.ex1
-rw-r--r--lib/pleroma/web/mastodon_api/views/notification_view.ex3
-rw-r--r--lib/pleroma/web/push/subscription.ex2
-rw-r--r--lib/pleroma/workers/poll_worker.ex45
8 files changed, 136 insertions, 26 deletions
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 7efbdc49a..32f13df69 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -72,6 +72,7 @@ defmodule Pleroma.Notification do
pleroma:emoji_reaction
pleroma:report
reblog
+ poll
}
def changeset(%Notification{} = notification, attrs) do
@@ -379,7 +380,7 @@ defmodule Pleroma.Notification do
notifications =
Enum.map(potential_receivers, fn user ->
do_send = do_send && user in enabled_receivers
- create_notification(activity, user, do_send)
+ create_notification(activity, user, do_send: do_send)
end)
|> Enum.reject(&is_nil/1)
@@ -435,15 +436,18 @@ defmodule Pleroma.Notification do
end
# TODO move to sql, too.
- def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do
- unless skip?(activity, user) do
+ def create_notification(%Activity{} = activity, %User{} = user, opts \\ []) do
+ do_send = Keyword.get(opts, :do_send, true)
+ type = Keyword.get(opts, :type, type_from_activity(activity))
+
+ unless skip?(activity, user, opts) do
{:ok, %{notification: notification}} =
Multi.new()
|> Multi.insert(:notification, %Notification{
user_id: user.id,
activity: activity,
seen: mark_as_read?(activity, user),
- type: type_from_activity(activity)
+ type: type
})
|> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction()
@@ -457,6 +461,28 @@ defmodule Pleroma.Notification do
end
end
+ def create_poll_notifications(%Activity{} = activity) do
+ with %Object{data: %{"type" => "Question", "actor" => actor} = data} <-
+ Object.normalize(activity) do
+ voters =
+ case data do
+ %{"voters" => voters} when is_list(voters) -> voters
+ _ -> []
+ end
+
+ notifications =
+ Enum.reduce([actor | voters], [], fn ap_id, acc ->
+ with %User{local: true} = user <- User.get_by_ap_id(ap_id) do
+ [create_notification(activity, user, type: "poll") | acc]
+ else
+ _ -> acc
+ end
+ end)
+
+ {:ok, notifications}
+ end
+ end
+
@doc """
Returns a tuple with 2 elements:
{notification-enabled receivers, currently disabled receivers (blocking / [thread] muting)}
@@ -572,8 +598,10 @@ defmodule Pleroma.Notification do
Enum.uniq(ap_ids) -- thread_muter_ap_ids
end
- @spec skip?(Activity.t(), User.t()) :: boolean()
- def skip?(%Activity{} = activity, %User{} = user) do
+ def skip?(activity, user, opts \\ [])
+
+ @spec skip?(Activity.t(), User.t(), Keyword.t()) :: boolean()
+ def skip?(%Activity{} = activity, %User{} = user, opts) do
[
:self,
:invisible,
@@ -581,17 +609,21 @@ defmodule Pleroma.Notification do
:recently_followed,
:filtered
]
- |> Enum.find(&skip?(&1, activity, user))
+ |> Enum.find(&skip?(&1, activity, user, opts))
end
- def skip?(_, _), do: false
+ def skip?(_activity, _user, _opts), do: false
- @spec skip?(atom(), Activity.t(), User.t()) :: boolean()
- def skip?(:self, %Activity{} = activity, %User{} = user) do
- activity.data["actor"] == user.ap_id
+ @spec skip?(atom(), Activity.t(), User.t(), Keyword.t()) :: boolean()
+ def skip?(:self, %Activity{} = activity, %User{} = user, opts) do
+ cond do
+ opts[:type] == "poll" -> false
+ activity.data["actor"] == user.ap_id -> true
+ true -> false
+ end
end
- def skip?(:invisible, %Activity{} = activity, _) do
+ def skip?(:invisible, %Activity{} = activity, _user, _opts) do
actor = activity.data["actor"]
user = User.get_cached_by_ap_id(actor)
User.invisible?(user)
@@ -600,15 +632,27 @@ defmodule Pleroma.Notification do
def skip?(
:block_from_strangers,
%Activity{} = activity,
- %User{notification_settings: %{block_from_strangers: true}} = user
+ %User{notification_settings: %{block_from_strangers: true}} = user,
+ opts
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
- !User.following?(follower, user)
+
+ cond do
+ opts[:type] == "poll" -> false
+ user.ap_id == actor -> false
+ !User.following?(follower, user) -> true
+ true -> false
+ end
end
# To do: consider defining recency in hours and checking FollowingRelationship with a single SQL
- def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity, %User{} = user) do
+ def skip?(
+ :recently_followed,
+ %Activity{data: %{"type" => "Follow"}} = activity,
+ %User{} = user,
+ _opts
+ ) do
actor = activity.data["actor"]
Notification.for_user(user)
@@ -618,9 +662,10 @@ defmodule Pleroma.Notification do
end)
end
- def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false
+ def skip?(:filtered, %{data: %{"type" => type}}, _user, _opts) when type in ["Follow", "Move"],
+ do: false
- def skip?(:filtered, activity, user) do
+ def skip?(:filtered, activity, user, _opts) do
object = Object.normalize(activity, fetch: false)
cond do
@@ -638,7 +683,7 @@ defmodule Pleroma.Notification do
end
end
- def skip?(_, _, _), do: false
+ def skip?(_type, _activity, _user, _opts), do: false
def mark_as_read?(activity, target_user) do
user = Activity.user_actor(activity)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 4c29dda35..19961a4a5 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -25,6 +25,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Web.Streamer
alias Pleroma.Web.WebFinger
alias Pleroma.Workers.BackgroundWorker
+ alias Pleroma.Workers.PollWorker
import Ecto.Query
import Pleroma.Web.ActivityPub.Utils
@@ -288,6 +289,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
_ <- notify_and_stream(activity),
+ :ok <- maybe_schedule_poll_notifications(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
else
@@ -302,6 +304,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ defp maybe_schedule_poll_notifications(activity) do
+ PollWorker.schedule_poll_end(activity)
+ :ok
+ end
+
@spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
additional = params[:additional] || %{}
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index b0ec84ade..dda48ea5f 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -24,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Push
alias Pleroma.Web.Streamer
+ alias Pleroma.Workers.PollWorker
require Logger
@@ -195,7 +196,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Set up notifications
@impl true
def handle(%{data: %{"type" => "Create"}} = activity, meta) do
- with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta),
+ with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta),
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
@@ -389,7 +390,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, object, meta}
end
- def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do
+ def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
actor = User.get_cached_by_ap_id(object.data["actor"])
recipient = User.get_cached_by_ap_id(hd(object.data["to"]))
@@ -424,7 +425,14 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
end
- def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
+ def handle_object_creation(%{"type" => "Question"} = object, activity, meta) do
+ with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
+ PollWorker.schedule_poll_end(activity)
+ {:ok, object, meta}
+ end
+ end
+
+ def handle_object_creation(%{"type" => "Answer"} = object_map, _activity, meta) do
with {:ok, object, meta} <- Pipeline.common_pipeline(object_map, meta) do
Object.increase_vote_count(
object.data["inReplyTo"],
@@ -436,15 +444,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
end
- def handle_object_creation(%{"type" => objtype} = object, meta)
- when objtype in ~w[Audio Video Question Event Article Note Page] do
+ def handle_object_creation(%{"type" => objtype} = object, _activity, meta)
+ when objtype in ~w[Audio Video Event Article Note Page] do
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
{:ok, object, meta}
end
end
# Nothing to do
- def handle_object_creation(object, meta) do
+ def handle_object_creation(object, _activity, meta) do
{:ok, object, meta}
end
diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex
index ec88eabe1..e4ce42f1c 100644
--- a/lib/pleroma/web/api_spec/operations/notification_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex
@@ -195,7 +195,8 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
"pleroma:chat_mention",
"pleroma:report",
"move",
- "follow_request"
+ "follow_request",
+ "poll"
],
description: """
The type of event that resulted in the notification.
diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
index 647ba661e..002d6b2ce 100644
--- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
@@ -50,6 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
favourite
move
pleroma:emoji_reaction
+ poll
}
def index(%{assigns: %{user: user}} = conn, params) do
params =
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index df9bedfed..35c636d4e 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -112,6 +112,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
"move" ->
put_target(response, activity, reading_user, %{})
+ "poll" ->
+ put_status(response, activity, reading_user, status_render_opts)
+
"pleroma:emoji_reaction" ->
response
|> put_status(parent_activity_fn.(), reading_user, status_render_opts)
diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex
index 4f6c9bc9f..35bf2e223 100644
--- a/lib/pleroma/web/push/subscription.ex
+++ b/lib/pleroma/web/push/subscription.ex
@@ -26,7 +26,7 @@ defmodule Pleroma.Web.Push.Subscription do
end
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
- @supported_alert_types ~w[follow favourite mention reblog pleroma:chat_mention pleroma:emoji_reaction]a
+ @supported_alert_types ~w[follow favourite mention reblog poll pleroma:chat_mention pleroma:emoji_reaction]a
defp alerts(%{data: %{alerts: alerts}}) do
alerts = Map.take(alerts, @supported_alert_types)
diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex
new file mode 100644
index 000000000..3423cc889
--- /dev/null
+++ b/lib/pleroma/workers/poll_worker.ex
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.PollWorker do
+ @moduledoc """
+ Generates notifications when a poll ends.
+ """
+ use Pleroma.Workers.WorkerHelper, queue: "poll_notifications"
+
+ alias Pleroma.Activity
+ alias Pleroma.Notification
+ alias Pleroma.Object
+
+ @impl Oban.Worker
+ def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do
+ with %Activity{} = activity <- find_poll_activity(activity_id) do
+ Notification.create_poll_notifications(activity)
+ end
+ end
+
+ defp find_poll_activity(activity_id) do
+ with nil <- Activity.get_by_id(activity_id) do
+ {:error, :poll_activity_not_found}
+ end
+ end
+
+ def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
+ with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <-
+ Object.normalize(activity),
+ {:ok, end_time} <- NaiveDateTime.from_iso8601(closed),
+ :gt <- NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) do
+ %{
+ op: "poll_end",
+ activity_id: activity_id
+ }
+ |> new(scheduled_at: end_time)
+ |> Oban.insert()
+ else
+ _ -> {:error, activity}
+ end
+ end
+
+ def schedule_poll_end(activity), do: {:error, activity}
+end