defmodule Mobilizon.Service.Workers.SendActivityRecapWorker do @moduledoc """ Worker to send activity recaps """ use Oban.Worker, queue: "notifications" alias Mobilizon.{Activities, Actors, Users} alias Mobilizon.Activities.Activity alias Mobilizon.Actors.Actor alias Mobilizon.Service.Notifier.Email alias Mobilizon.Storage.Repo alias Mobilizon.Users.{Setting, User} require Logger import Mobilizon.Service.DateTime, only: [ is_between_hours?: 1, is_between_hours_on_first_day?: 1, is_delay_ok_since_last_notification_sent?: 1 ] @impl Oban.Worker def perform(%Job{}) do Logger.info("Sending scheduled activity recap") Repo.transaction( fn -> Users.stream_users_for_recap() |> Enum.to_list() |> Repo.preload([:settings, :activity_settings]) |> Enum.filter(&filter_elegible_users/1) |> Enum.map(fn %User{} = user -> %{ activities: activities_for_user(user), user: user } end) |> Enum.filter(fn %{activities: activities, user: _user} -> length(activities) > 0 end) |> Enum.map(fn %{ activities: activities, user: %User{settings: %Setting{group_notifications: group_notifications}} = user } -> Email.send(user, activities, recap: group_notifications) end) end, timeout: :infinity ) end defp activities_for_user( %User{settings: %Setting{last_notification_sent: last_notification_sent}} = user ) do user |> Users.get_actors_for_user() |> Enum.flat_map(&group_memberships(&1, last_notification_sent)) |> Enum.uniq() end defp group_memberships(%Actor{id: actor_id} = actor, last_notification_sent) do actor |> group_memberships_for_actor() |> Enum.uniq() |> Enum.flat_map(&activities_for_group(&1, actor_id, last_notification_sent)) end defp group_memberships_for_actor(%Actor{} = actor) do Actors.list_groups_member_of(actor) end defp activities_for_group( %Actor{id: group_id, type: :Group}, actor_asking_id, last_notification_sent ) do group_id |> Activities.list_group_activities_for_recap(actor_asking_id, last_notification_sent) # Don't send my own activities |> Enum.filter(fn %Activity{author: %Actor{id: author_id}} -> author_id != actor_asking_id end) end defp filter_elegible_users(%User{ settings: %Setting{last_notification_sent: nil, group_notifications: :one_hour} }) do true end defp filter_elegible_users(%User{ settings: %Setting{ last_notification_sent: %DateTime{} = last_notification_sent, group_notifications: :one_hour } }) do is_delay_ok_since_last_notification_sent?(last_notification_sent) end # If we're between notification hours defp filter_elegible_users(%User{ settings: %Setting{ group_notifications: :one_day, timezone: timezone } }) do is_between_hours?(timezone: timezone || "Etc/UTC") end # If we're on the first day of the week between notification hours defp filter_elegible_users(%User{ locale: locale, settings: %Setting{ group_notifications: :one_week, timezone: timezone } }) do is_between_hours_on_first_day?(timezone: timezone || "Etc/UTC", locale: locale) end end