Send notifications to event organizer when new comment is posted

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-06-27 11:21:34 +02:00
parent 93297931bb
commit 3ed25bab81
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
9 changed files with 161 additions and 64 deletions

View file

@ -214,6 +214,17 @@ defmodule Mobilizon.Events do
|> Repo.exists?() |> Repo.exists?()
end end
@doc """
Gets an event by its UUID, with all associations loaded.
"""
@spec get_event_by_uuid_with_preload(String.t()) :: Event.t() | nil
def get_event_by_uuid_with_preload(uuid) do
uuid
|> event_by_uuid_query()
|> preload_for_event()
|> Repo.one()
end
@doc """ @doc """
Gets an event by its UUID, with all associations loaded. Gets an event by its UUID, with all associations loaded.
""" """

View file

@ -159,10 +159,11 @@ defmodule Mobilizon.Service.Activity.Comment do
%Comment{ %Comment{
actor_id: actor_id, actor_id: actor_id,
in_reply_to_comment_id: in_reply_to_comment_id, in_reply_to_comment_id: in_reply_to_comment_id,
id: comment_id id: comment_id,
}, uuid: comment_uuid
} = comment,
%Event{ %Event{
uuid: uuid, uuid: event_uuid,
title: title, title: title,
attributed_to: nil, attributed_to: nil,
organizer_actor_id: organizer_actor_id organizer_actor_id: organizer_actor_id
@ -174,8 +175,10 @@ defmodule Mobilizon.Service.Activity.Comment do
"subject" => :event_new_comment, "subject" => :event_new_comment,
"subject_params" => %{ "subject_params" => %{
event_title: title, event_title: title,
event_uuid: uuid, event_uuid: event_uuid,
comment_reply_to: !is_nil(in_reply_to_comment_id) comment_reply_to: !is_nil(in_reply_to_comment_id),
comment_uuid: comment_uuid,
comment_reply_to_uuid: reply_to_comment_uuid(comment)
}, },
"author_id" => actor_id, "author_id" => actor_id,
"object_id" => to_string(comment_id) "object_id" => to_string(comment_id)
@ -185,4 +188,10 @@ defmodule Mobilizon.Service.Activity.Comment do
end end
defp notify(_, _, _, _), do: {:ok, :skipped} defp notify(_, _, _, _), do: {:ok, :skipped}
@spec reply_to_comment_uuid(Comment.t()) :: String.t() | nil
defp reply_to_comment_uuid(%Comment{in_reply_to_comment: %Comment{uuid: comment_reply_to_uuid}}),
do: comment_reply_to_uuid
defp reply_to_comment_uuid(%Comment{in_reply_to_comment: _}), do: nil
end end

View file

@ -45,6 +45,35 @@ defmodule Mobilizon.Service.Activity.Renderer.Comment do
), ),
url: event_url(activity) url: event_url(activity)
} }
:event_new_comment ->
if activity.subject_params["comment_reply_to"] do
%{
body:
dgettext(
"activity",
"%{profile} has posted a new reply under your event %{event}.",
%{
profile: profile,
event: event_title(activity)
}
),
url: event_comment_url(activity)
}
else
%{
body:
dgettext(
"activity",
"%{profile} has posted a new comment under your event %{event}.",
%{
profile: profile,
event: event_title(activity)
}
),
url: event_comment_url(activity)
}
end
end end
end end
@ -56,6 +85,18 @@ defmodule Mobilizon.Service.Activity.Renderer.Comment do
) )
end end
defp event_comment_url(activity) do
"#{event_url(activity)}#comment-#{comment_uuid(activity)}"
end
defp comment_uuid(activity) do
if activity.subject_params["comment_reply_to"] do
"#{activity.subject_params["reply_to_comment_uuid"]}-#{activity.subject_params["comment_uuid"]}"
else
activity.subject_params["comment_uuid"]
end
end
defp profile(activity), do: Actor.display_name_and_username(activity.author) defp profile(activity), do: Actor.display_name_and_username(activity.author)
defp event_title(activity), do: activity.subject_params["event_title"] defp event_title(activity), do: activity.subject_params["event_title"]
end end

View file

@ -56,7 +56,8 @@ defmodule Mobilizon.Service.Notifier.Email do
@always_direct_subjects [ @always_direct_subjects [
:participation_event_comment, :participation_event_comment,
:event_comment_mention, :event_comment_mention,
:discussion_mention :discussion_mention,
:event_new_comment
] ]
@spec can_send_activity?(Activity.t(), User.t(), Keyword.t()) :: boolean() @spec can_send_activity?(Activity.t(), User.t(), Keyword.t()) :: boolean()
@ -96,30 +97,24 @@ defmodule Mobilizon.Service.Notifier.Email do
when subject in @always_direct_subjects, when subject in @always_direct_subjects,
do: true do: true
defp match_group_notifications_setting(
group_notifications,
_subject,
last_notification_sent,
options
) do
cond do
# This is a recap
Keyword.get(options, :recap, false) != false ->
true
# First notification EVER! # First notification EVER!
group_notifications == :one_hour && is_nil(last_notification_sent) -> defp match_group_notifications_setting(:one_hour, _, last_notification_sent, _)
true when is_nil(last_notification_sent),
do: true
# Delay ok since last notification # Delay ok since last notification
group_notifications == :one_hour && defp match_group_notifications_setting(:one_hour, _, %DateTime{} = last_notification_sent, _) do
is_delay_ok_since_last_notification_sent(last_notification_sent) -> is_delay_ok_since_last_notification_sent(last_notification_sent)
true
# Otherwise, no thanks
true ->
false
end end
# This is a recap
defp match_group_notifications_setting(
_group_notifications,
_subject,
_last_notification_sent,
options
) do
Keyword.get(options, :recap, false) != false
end end
@default_behavior %{ @default_behavior %{

View file

@ -23,10 +23,13 @@ defmodule Mobilizon.Service.Notifier.Filter do
defp enabled?(nil, activity_setting, get_default), do: get_default.(activity_setting) defp enabled?(nil, activity_setting, get_default), do: get_default.(activity_setting)
defp enabled?(%ActivitySetting{enabled: enabled}, _activity_setting, _get_default), do: enabled defp enabled?(%ActivitySetting{enabled: enabled}, _activity_setting, _get_default), do: enabled
# Comment mention # Mention
defp map_activity_to_activity_setting(%Activity{subject: :event_comment_mention}), defp map_activity_to_activity_setting(%Activity{subject: :event_comment_mention}),
do: "event_comment_mention" do: "event_comment_mention"
defp map_activity_to_activity_setting(%Activity{subject: :discussion_mention}),
do: "discussion_mention"
# Participation # Participation
@spec map_activity_to_activity_setting(Activity.t()) :: String.t() | false @spec map_activity_to_activity_setting(Activity.t()) :: String.t() | false
defp map_activity_to_activity_setting(%Activity{subject: :participation_event_updated}), defp map_activity_to_activity_setting(%Activity{subject: :participation_event_updated}),
@ -42,6 +45,9 @@ defmodule Mobilizon.Service.Notifier.Filter do
defp map_activity_to_activity_setting(%Activity{subject: :event_new_participation}), defp map_activity_to_activity_setting(%Activity{subject: :event_new_participation}),
do: "event_new_participation" do: "event_new_participation"
defp map_activity_to_activity_setting(%Activity{subject: :event_new_comment}),
do: "event_new_comment"
# Event # Event
defp map_activity_to_activity_setting(%Activity{subject: :event_created}), do: "event_created" defp map_activity_to_activity_setting(%Activity{subject: :event_created}), do: "event_created"
defp map_activity_to_activity_setting(%Activity{type: :event}), do: "event_updated" defp map_activity_to_activity_setting(%Activity{type: :event}), do: "event_updated"
@ -60,7 +66,7 @@ defmodule Mobilizon.Service.Notifier.Filter do
defp map_activity_to_activity_setting(%Activity{subject: :member_request}), defp map_activity_to_activity_setting(%Activity{subject: :member_request}),
do: "member_request" do: "member_request"
defp map_activity_to_activity_setting(%Activity{type: :member}), do: "member" defp map_activity_to_activity_setting(%Activity{type: :member}), do: "member_updated"
defp map_activity_to_activity_setting(_), do: false defp map_activity_to_activity_setting(_), do: false
end end

View file

@ -5,6 +5,8 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilder do
alias Mobilizon.Activities.Activity alias Mobilizon.Activities.Activity
alias Mobilizon.{Actors, Events, Users} alias Mobilizon.{Actors, Events, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Events.Event
alias Mobilizon.Service.Notifier alias Mobilizon.Service.Notifier
use Mobilizon.Service.Workers.Helper, queue: "activity" use Mobilizon.Service.Workers.Helper, queue: "activity"
@ -66,6 +68,18 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilder do
|> users_from_actor_ids(Keyword.fetch!(options, :author_id)) |> users_from_actor_ids(Keyword.fetch!(options, :author_id))
end end
defp users_to_notify(
%{"subject" => "event_new_comment", "subject_params" => %{"event_uuid" => event_uuid}},
options
) do
event_uuid
|> Events.get_event_by_uuid_with_preload()
|> (fn %Event{organizer_actor: %Actor{id: actor_id}} -> [actor_id] end).()
|> users_from_actor_ids(Keyword.fetch!(options, :author_id))
end
defp users_to_notify(_, _), do: []
@spec users_from_actor_ids(list(), integer() | String.t()) :: list(Users.t()) @spec users_from_actor_ids(list(), integer() | String.t()) :: list(Users.t())
defp users_from_actor_ids(actor_ids, author_id) do defp users_from_actor_ids(actor_ids, author_id) do
actor_ids actor_ids

View file

@ -29,4 +29,37 @@
</a>" </a>"
} }
) |> raw %> ) |> raw %>
<% :event_new_comment -> %>
<%= if @activity.subject_params["comment_reply_to"] do %>
<%=
dgettext("activity", "%{profile} has posted a new reply under your event %{event}.",
%{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{
page_url(
Mobilizon.Web.Endpoint,
:event,
@activity.subject_params["event_uuid"]
) |> URI.decode()}#comment-#{@activity.subject_params["comment_reply_to_uuid"]}-#{@activity.subject_params["comment_uuid"]}\">
#{@activity.subject_params["event_title"]}
</a>"
}
) |> raw %>
<% else %>
<%=
dgettext("activity", "%{profile} has posted a new comment under your event %{event}.",
%{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{
page_url(
Mobilizon.Web.Endpoint,
:event,
@activity.subject_params["event_uuid"]
) |> URI.decode()}#comment-#{@activity.subject_params["comment_uuid"]}\">
#{@activity.subject_params["event_title"]}
</a>"
}
) |> raw %>
<% end %>
<% end %> <% end %>

View file

@ -1,34 +1,4 @@
<%= case @activity.subject do %><% :discussion_created -> %><%= dgettext("activity", "%{profile} created the discussion %{discussion}.", <%= case @activity.subject do %><% :event_comment_mention -> %><%= dgettext("activity", "%{profile} mentionned you in a comment under event %{event}.",
%{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
discussion: @activity.subject_params["discussion_title"]
}
) %>
<%= page_url(Mobilizon.Web.Endpoint, :discussion, Mobilizon.Actors.Actor.preferred_username_and_domain(@activity.group), @activity.subject_params["discussion_slug"]) |> URI.decode() %><% :discussion_replied -> %><%= dgettext("activity", "%{profile} replied to the discussion %{discussion}.",
%{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
discussion: @activity.subject_params["discussion_title"]
}
) %>
<%= page_url(Mobilizon.Web.Endpoint, :discussion, Mobilizon.Actors.Actor.preferred_username_and_domain(@activity.group), @activity.subject_params["discussion_slug"]) |> URI.decode() %><% :discussion_renamed -> %><%= dgettext("activity", "%{profile} renamed the discussion %{discussion}.",
%{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
discussion: @activity.subject_params["discussion_title"]
}
) %>
<%= page_url(Mobilizon.Web.Endpoint, :discussion, Mobilizon.Actors.Actor.preferred_username_and_domain(@activity.group), @activity.subject_params["discussion_slug"]) |> URI.decode() %><% :discussion_archived -> %><%= dgettext("activity", "%{profile} archived the discussion %{discussion}.",
%{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
discussion: @activity.subject_params["discussion_title"]
}
) %>
<%= page_url(Mobilizon.Web.Endpoint, :discussion, Mobilizon.Actors.Actor.preferred_username_and_domain(@activity.group), @activity.subject_params["discussion_slug"]) |> URI.decode() %><% :discussion_deleted -> %><%= dgettext("activity", "%{profile} deleted the discussion %{discussion}.",
%{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
discussion: @activity.subject_params["discussion_title"]
}
) %>
<%= page_url(Mobilizon.Web.Endpoint, :discussion, Mobilizon.Actors.Actor.preferred_username_and_domain(@activity.group), @activity.subject_params["discussion_slug"]) |> URI.decode() %><% :event_comment_mention -> %><%= dgettext("activity", "%{profile} mentionned you in a comment under event %{event}.",
%{ %{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author), profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
event: @activity.subject_params["event_title"] event: @activity.subject_params["event_title"]
@ -40,4 +10,16 @@
event: @activity.subject_params["event_title"] event: @activity.subject_params["event_title"]
} }
) %> ) %>
<%= page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% end %> <%= page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode() %><% :event_new_comment -> %><%= if @activity.subject_params["comment_reply_to"] do %><%=dgettext("activity", "%{profile} has posted a new reply under your event %{event}.",
%{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
event: @activity.subject_params["event_title"]
}
) %>
<%= "#{page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode()}#comment-#{@activity.subject_params["comment_reply_to_uuid"]}-#{@activity.subject_params["comment_uuid"]}" %><% else %><%= dgettext("activity", "%{profile} has posted a new comment under your event %{event}.",
%{
profile: Mobilizon.Actors.Actor.display_name_and_username(@activity.author),
event: @activity.subject_params["event_title"]
}
) %>
<%= "#{page_url(Mobilizon.Web.Endpoint, :event, @activity.subject_params["event_uuid"]) |> URI.decode()}#comment-#{@activity.subject_params["comment_uuid"]}"%><% end %><% end %>

View file

@ -18,7 +18,9 @@ defmodule Mobilizon.Service.Activity.CommentTest do
describe "handle comment with mentions" do describe "handle comment with mentions" do
test "with no mentions" do test "with no mentions" do
%Event{title: event_title, uuid: event_uuid} = event = insert(:event) %Event{title: event_title, uuid: event_uuid} = event = insert(:event)
%Comment{id: comment_id, actor_id: author_id} = comment = insert(:comment, event: event)
%Comment{id: comment_id, actor_id: author_id, uuid: comment_uuid} =
comment = insert(:comment, event: event)
assert {:ok, [organizer: :enqueued, announcement: :skipped, mentionned: :skipped]} == assert {:ok, [organizer: :enqueued, announcement: :skipped, mentionned: :skipped]} ==
CommentActivity.insert_activity(comment) CommentActivity.insert_activity(comment)
@ -37,9 +39,11 @@ defmodule Mobilizon.Service.Activity.CommentTest do
"op" => "legacy_notify", "op" => "legacy_notify",
"subject" => "event_new_comment", "subject" => "event_new_comment",
"subject_params" => %{ "subject_params" => %{
"comment_reply_to_uuid" => nil,
"event_title" => event_title, "event_title" => event_title,
"event_uuid" => event_uuid, "event_uuid" => event_uuid,
"comment_reply_to" => false "comment_reply_to" => false,
"comment_uuid" => comment_uuid
}, },
"type" => "comment" "type" => "comment"
} }
@ -51,7 +55,7 @@ defmodule Mobilizon.Service.Activity.CommentTest do
%Actor{id: actor_id} = actor = insert(:actor, user: user) %Actor{id: actor_id} = actor = insert(:actor, user: user)
%Event{uuid: event_uuid, title: event_title} = event = insert(:event) %Event{uuid: event_uuid, title: event_title} = event = insert(:event)
%Comment{id: comment_id, actor_id: author_id} = %Comment{id: comment_id, actor_id: author_id, uuid: comment_uuid} =
comment = insert(:comment, text: "Hey @you", event: event) comment = insert(:comment, text: "Hey @you", event: event)
comment = %Comment{ comment = %Comment{
@ -90,9 +94,11 @@ defmodule Mobilizon.Service.Activity.CommentTest do
"op" => "legacy_notify", "op" => "legacy_notify",
"subject" => "event_new_comment", "subject" => "event_new_comment",
"subject_params" => %{ "subject_params" => %{
"comment_reply_to_uuid" => nil,
"event_title" => event_title, "event_title" => event_title,
"event_uuid" => event_uuid, "event_uuid" => event_uuid,
"comment_reply_to" => false "comment_reply_to" => false,
"comment_uuid" => comment_uuid
}, },
"type" => "comment" "type" => "comment"
} }