diff options
author | Haelwenn <contact+git.pleroma.social@hacktivis.me> | 2021-08-09 10:02:37 +0000 |
---|---|---|
committer | Haelwenn <contact+git.pleroma.social@hacktivis.me> | 2021-08-09 10:02:37 +0000 |
commit | 901204df2292419378a486a97fea9d3c70903a20 (patch) | |
tree | e7b2b68201819dce06079d4decfacaa4c8aa1043 /lib | |
parent | 6384d7803520f633a66d1cb9f76e3540863f92fb (diff) | |
parent | 85d71d4f1d433a168eb136ad88b651b6c1d1a4fc (diff) | |
download | pleroma-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.ex | 81 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 7 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/side_effects.ex | 20 | ||||
-rw-r--r-- | lib/pleroma/web/api_spec/operations/notification_operation.ex | 3 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/notification_controller.ex | 1 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/notification_view.ex | 3 | ||||
-rw-r--r-- | lib/pleroma/web/push/subscription.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/workers/poll_worker.ex | 45 |
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 |