defmodule Pleroma.ModerationLog do use Ecto.Schema alias Pleroma.Activity alias Pleroma.ModerationLog alias Pleroma.Repo alias Pleroma.User import Ecto.Query schema "moderation_log" do field(:data, :map) timestamps() end def get_all(params) do params |> get_all_query() |> maybe_filter_by_date(params) |> Repo.all() end defp maybe_filter_by_date(query, %{start_date: nil, end_date: nil}), do: query defp maybe_filter_by_date(query, %{start_date: start_date, end_date: nil}) do from(q in query, where: q.inserted_at >= ^parse_datetime(start_date) ) end defp maybe_filter_by_date(query, %{start_date: nil, end_date: end_date}) do from(q in query, where: q.inserted_at <= ^parse_datetime(end_date) ) end defp maybe_filter_by_date(query, %{start_date: start_date, end_date: end_date}) do from(q in query, where: q.inserted_at >= ^parse_datetime(start_date), where: q.inserted_at <= ^parse_datetime(end_date) ) end defp get_all_query(%{page: page, page_size: page_size}) do from(q in __MODULE__, order_by: [desc: q.inserted_at], limit: ^page_size, offset: ^((page - 1) * page_size) ) end defp parse_datetime(datetime) do {:ok, parsed_datetime, _} = DateTime.from_iso8601(datetime) parsed_datetime end def insert_log(%{ actor: %User{} = actor, subject: %User{} = subject, action: action, permission: permission }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), subject: user_to_map(subject), action: action, permission: permission } }) end def insert_log(%{ actor: %User{} = actor, action: "report_update", subject: %Activity{data: %{"type" => "Flag"}} = subject }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: "report_update", subject: report_to_map(subject) } }) end def insert_log(%{ actor: %User{} = actor, action: "report_response", subject: %Activity{} = subject, text: text }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: "report_response", subject: report_to_map(subject), text: text } }) end def insert_log(%{ actor: %User{} = actor, action: "status_update", subject: %Activity{} = subject, sensitive: sensitive, visibility: visibility }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: "status_update", subject: status_to_map(subject), sensitive: sensitive, visibility: visibility } }) end def insert_log(%{ actor: %User{} = actor, action: "status_delete", subject_id: subject_id }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: "status_delete", subject_id: subject_id } }) end @spec insert_log(%{actor: User, subject: User, action: String.t()}) :: {:ok, ModerationLog} | {:error, any} def insert_log(%{actor: %User{} = actor, subject: subject, action: action}) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: action, subject: user_to_map(subject) } }) end @spec insert_log(%{actor: User, subjects: [User], action: String.t()}) :: {:ok, ModerationLog} | {:error, any} def insert_log(%{actor: %User{} = actor, subjects: subjects, action: action}) do subjects = Enum.map(subjects, &user_to_map/1) Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: action, subjects: subjects } }) end def insert_log(%{ actor: %User{} = actor, followed: %User{} = followed, follower: %User{} = follower, action: "follow" }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: "follow", followed: user_to_map(followed), follower: user_to_map(follower) } }) end def insert_log(%{ actor: %User{} = actor, followed: %User{} = followed, follower: %User{} = follower, action: "unfollow" }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: "unfollow", followed: user_to_map(followed), follower: user_to_map(follower) } }) end def insert_log(%{ actor: %User{} = actor, nicknames: nicknames, tags: tags, action: action }) do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), nicknames: nicknames, tags: tags, action: action } }) end def insert_log(%{ actor: %User{} = actor, action: action, target: target }) when action in ["relay_follow", "relay_unfollow"] do Repo.insert(%ModerationLog{ data: %{ actor: user_to_map(actor), action: action, target: target } }) end defp user_to_map(%User{} = user) do user |> Map.from_struct() |> Map.take([:id, :nickname]) |> Map.put(:type, "user") end defp report_to_map(%Activity{} = report) do %{ type: "report", id: report.id, state: report.data["state"] } end defp status_to_map(%Activity{} = status) do %{ type: "status", id: status.id } end def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => action, "followed" => %{"nickname" => followed_nickname}, "follower" => %{"nickname" => follower_nickname} } }) do "@#{actor_nickname} made @#{follower_nickname} #{action} @#{followed_nickname}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "delete", "subject" => %{"nickname" => subject_nickname, "type" => "user"} } }) do "@#{actor_nickname} deleted user @#{subject_nickname}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "create", "subjects" => subjects } }) do nicknames = subjects |> Enum.map(&"@#{&1["nickname"]}") |> Enum.join(", ") "@#{actor_nickname} created users: #{nicknames}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "activate", "subject" => %{"nickname" => subject_nickname, "type" => "user"} } }) do "@#{actor_nickname} activated user @#{subject_nickname}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "deactivate", "subject" => %{"nickname" => subject_nickname, "type" => "user"} } }) do "@#{actor_nickname} deactivated user @#{subject_nickname}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "nicknames" => nicknames, "tags" => tags, "action" => "tag" } }) do nicknames_string = nicknames |> Enum.map(&"@#{&1}") |> Enum.join(", ") tags_string = tags |> Enum.join(", ") "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_string}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "nicknames" => nicknames, "tags" => tags, "action" => "untag" } }) do nicknames_string = nicknames |> Enum.map(&"@#{&1}") |> Enum.join(", ") tags_string = tags |> Enum.join(", ") "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_string}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "grant", "subject" => %{"nickname" => subject_nickname}, "permission" => permission } }) do "@#{actor_nickname} made @#{subject_nickname} #{permission}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "revoke", "subject" => %{"nickname" => subject_nickname}, "permission" => permission } }) do "@#{actor_nickname} revoked #{permission} role from @#{subject_nickname}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "relay_follow", "target" => target } }) do "@#{actor_nickname} followed relay: #{target}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "relay_unfollow", "target" => target } }) do "@#{actor_nickname} unfollowed relay: #{target}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "report_update", "subject" => %{"id" => subject_id, "state" => state, "type" => "report"} } }) do "@#{actor_nickname} updated report ##{subject_id} with '#{state}' state" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "report_response", "subject" => %{"id" => subject_id, "type" => "report"}, "text" => text } }) do "@#{actor_nickname} responded with '#{text}' to report ##{subject_id}" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "status_update", "subject" => %{"id" => subject_id, "type" => "status"}, "sensitive" => nil, "visibility" => visibility } }) do "@#{actor_nickname} updated status ##{subject_id}, set visibility: '#{visibility}'" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "status_update", "subject" => %{"id" => subject_id, "type" => "status"}, "sensitive" => sensitive, "visibility" => nil } }) do "@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}'" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "status_update", "subject" => %{"id" => subject_id, "type" => "status"}, "sensitive" => sensitive, "visibility" => visibility } }) do "@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}', visibility: '#{ visibility }'" end @spec get_log_entry_message(ModerationLog) :: String.t() def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, "action" => "status_delete", "subject_id" => subject_id } }) do "@#{actor_nickname} deleted status ##{subject_id}" end end