679 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			679 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
# Pleroma: A lightweight social networking server
 | 
						|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
 | 
						|
# SPDX-License-Identifier: AGPL-3.0-only
 | 
						|
 | 
						|
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
 | 
						|
    base_query =
 | 
						|
      get_all_query()
 | 
						|
      |> maybe_filter_by_date(params)
 | 
						|
      |> maybe_filter_by_user(params)
 | 
						|
      |> maybe_filter_by_search(params)
 | 
						|
 | 
						|
    query_with_pagination = base_query |> paginate_query(params)
 | 
						|
 | 
						|
    %{
 | 
						|
      items: Repo.all(query_with_pagination),
 | 
						|
      count: Repo.aggregate(base_query, :count, :id)
 | 
						|
    }
 | 
						|
  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 maybe_filter_by_user(query, %{user_id: nil}), do: query
 | 
						|
 | 
						|
  defp maybe_filter_by_user(query, %{user_id: user_id}) do
 | 
						|
    from(q in query,
 | 
						|
      where: fragment("(?)->'actor'->>'id' = ?", q.data, ^user_id)
 | 
						|
    )
 | 
						|
  end
 | 
						|
 | 
						|
  defp maybe_filter_by_search(query, %{search: search}) when is_nil(search) or search == "",
 | 
						|
    do: query
 | 
						|
 | 
						|
  defp maybe_filter_by_search(query, %{search: search}) do
 | 
						|
    from(q in query,
 | 
						|
      where: fragment("(?)->>'message' ILIKE ?", q.data, ^"%#{search}%")
 | 
						|
    )
 | 
						|
  end
 | 
						|
 | 
						|
  defp paginate_query(query, %{page: page, page_size: page_size}) do
 | 
						|
    from(q in query,
 | 
						|
      limit: ^page_size,
 | 
						|
      offset: ^((page - 1) * page_size)
 | 
						|
    )
 | 
						|
  end
 | 
						|
 | 
						|
  defp get_all_query do
 | 
						|
    from(q in __MODULE__,
 | 
						|
      order_by: [desc: q.inserted_at]
 | 
						|
    )
 | 
						|
  end
 | 
						|
 | 
						|
  defp parse_datetime(datetime) do
 | 
						|
    {:ok, parsed_datetime, _} = DateTime.from_iso8601(datetime)
 | 
						|
 | 
						|
    parsed_datetime
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, subject: [User], action: String.t(), permission: String.t()}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        subject: subjects,
 | 
						|
        action: action,
 | 
						|
        permission: permission
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "subject" => user_to_map(subjects),
 | 
						|
        "action" => action,
 | 
						|
        "permission" => permission,
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, subject: User, action: String.t()}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        action: "report_update",
 | 
						|
        subject: %Activity{data: %{"type" => "Flag"}} = subject
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => "report_update",
 | 
						|
        "subject" => report_to_map(subject),
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        action: "report_note",
 | 
						|
        subject: %Activity{} = subject,
 | 
						|
        text: text
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => "report_note",
 | 
						|
        "subject" => report_to_map(subject),
 | 
						|
        "text" => text
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        action: "report_note_delete",
 | 
						|
        subject: %Activity{} = subject,
 | 
						|
        text: text
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => "report_note_delete",
 | 
						|
        "subject" => report_to_map(subject),
 | 
						|
        "text" => text
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{
 | 
						|
          actor: User,
 | 
						|
          subject: Activity,
 | 
						|
          action: String.t(),
 | 
						|
          sensitive: String.t(),
 | 
						|
          visibility: String.t()
 | 
						|
        }) :: {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        action: "status_update",
 | 
						|
        subject: %Activity{} = subject,
 | 
						|
        sensitive: sensitive,
 | 
						|
        visibility: visibility
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => "status_update",
 | 
						|
        "subject" => status_to_map(subject),
 | 
						|
        "sensitive" => sensitive,
 | 
						|
        "visibility" => visibility,
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        action: "status_delete",
 | 
						|
        subject_id: subject_id
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => "status_delete",
 | 
						|
        "subject_id" => subject_id,
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  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
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => action,
 | 
						|
        "subject" => user_to_map(subject),
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  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)
 | 
						|
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => action,
 | 
						|
        "subjects" => subjects,
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, action: String.t(), followed: User, follower: User}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        followed: %User{} = followed,
 | 
						|
        follower: %User{} = follower,
 | 
						|
        action: "follow"
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => "follow",
 | 
						|
        "followed" => user_to_map(followed),
 | 
						|
        "follower" => user_to_map(follower),
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, action: String.t(), followed: User, follower: User}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        followed: %User{} = followed,
 | 
						|
        follower: %User{} = follower,
 | 
						|
        action: "unfollow"
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => "unfollow",
 | 
						|
        "followed" => user_to_map(followed),
 | 
						|
        "follower" => user_to_map(follower),
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{
 | 
						|
          actor: User,
 | 
						|
          action: String.t(),
 | 
						|
          nicknames: [String.t()],
 | 
						|
          tags: [String.t()]
 | 
						|
        }) :: {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        nicknames: nicknames,
 | 
						|
        tags: tags,
 | 
						|
        action: action
 | 
						|
      }) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "nicknames" => nicknames,
 | 
						|
        "tags" => tags,
 | 
						|
        "action" => action,
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, action: String.t(), target: String.t()}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{
 | 
						|
        actor: %User{} = actor,
 | 
						|
        action: action,
 | 
						|
        target: target
 | 
						|
      })
 | 
						|
      when action in ["relay_follow", "relay_unfollow"] do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => user_to_map(actor),
 | 
						|
        "action" => action,
 | 
						|
        "target" => target,
 | 
						|
        "message" => ""
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) ::
 | 
						|
          {:ok, ModerationLog} | {:error, any}
 | 
						|
  def insert_log(%{actor: %User{} = actor, action: "chat_message_delete", subject_id: subject_id}) do
 | 
						|
    %ModerationLog{
 | 
						|
      data: %{
 | 
						|
        "actor" => %{"nickname" => actor.nickname},
 | 
						|
        "action" => "chat_message_delete",
 | 
						|
        "subject_id" => subject_id
 | 
						|
      }
 | 
						|
    }
 | 
						|
    |> insert_log_entry_with_message()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
 | 
						|
  defp insert_log_entry_with_message(entry) do
 | 
						|
    entry.data["message"]
 | 
						|
    |> put_in(get_log_entry_message(entry))
 | 
						|
    |> Repo.insert()
 | 
						|
  end
 | 
						|
 | 
						|
  defp user_to_map(users) when is_list(users) do
 | 
						|
    users |> Enum.map(&user_to_map/1)
 | 
						|
  end
 | 
						|
 | 
						|
  defp user_to_map(%User{} = user) do
 | 
						|
    user
 | 
						|
    |> Map.from_struct()
 | 
						|
    |> Map.take([:id, :nickname])
 | 
						|
    |> Map.new(fn {k, v} -> {Atom.to_string(k), v} end)
 | 
						|
    |> 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" => subjects
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} deleted users: #{users_to_nicknames_string(subjects)}"
 | 
						|
  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
 | 
						|
    "@#{actor_nickname} created users: #{users_to_nicknames_string(subjects)}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "activate",
 | 
						|
          "subject" => users
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} activated users: #{users_to_nicknames_string(users)}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "deactivate",
 | 
						|
          "subject" => users
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} deactivated users: #{users_to_nicknames_string(users)}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "approve",
 | 
						|
          "subject" => users
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
 | 
						|
  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
 | 
						|
    tags_string = tags |> Enum.join(", ")
 | 
						|
 | 
						|
    "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_to_string(nicknames)}"
 | 
						|
  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
 | 
						|
    tags_string = tags |> Enum.join(", ")
 | 
						|
 | 
						|
    "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_to_string(nicknames)}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "grant",
 | 
						|
          "subject" => users,
 | 
						|
          "permission" => permission
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} made #{users_to_nicknames_string(users)} #{permission}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "revoke",
 | 
						|
          "subject" => users,
 | 
						|
          "permission" => permission
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} revoked #{permission} role from #{users_to_nicknames_string(users)}"
 | 
						|
  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_note",
 | 
						|
          "subject" => %{"id" => subject_id, "type" => "report"},
 | 
						|
          "text" => text
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} added note '#{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" => "report_note_delete",
 | 
						|
          "subject" => %{"id" => subject_id, "type" => "report"},
 | 
						|
          "text" => text
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} deleted note '#{text}' from 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
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "force_password_reset",
 | 
						|
          "subject" => subjects
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} forced password reset for users: #{users_to_nicknames_string(subjects)}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "confirm_email",
 | 
						|
          "subject" => subjects
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} confirmed email for users: #{users_to_nicknames_string(subjects)}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "resend_confirmation_email",
 | 
						|
          "subject" => subjects
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} re-sent confirmation email for users: #{
 | 
						|
      users_to_nicknames_string(subjects)
 | 
						|
    }"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "updated_users",
 | 
						|
          "subject" => subjects
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"
 | 
						|
  end
 | 
						|
 | 
						|
  @spec get_log_entry_message(ModerationLog) :: String.t()
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "chat_message_delete",
 | 
						|
          "subject_id" => subject_id
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} deleted chat message ##{subject_id}"
 | 
						|
  end
 | 
						|
 | 
						|
  def get_log_entry_message(%ModerationLog{
 | 
						|
        data: %{
 | 
						|
          "actor" => %{"nickname" => actor_nickname},
 | 
						|
          "action" => "create_backup",
 | 
						|
          "subject" => %{"nickname" => user_nickname}
 | 
						|
        }
 | 
						|
      }) do
 | 
						|
    "@#{actor_nickname} requested account backup for @#{user_nickname}"
 | 
						|
  end
 | 
						|
 | 
						|
  defp nicknames_to_string(nicknames) do
 | 
						|
    nicknames
 | 
						|
    |> Enum.map(&"@#{&1}")
 | 
						|
    |> Enum.join(", ")
 | 
						|
  end
 | 
						|
 | 
						|
  defp users_to_nicknames_string(users) do
 | 
						|
    users
 | 
						|
    |> Enum.map(&"@#{&1["nickname"]}")
 | 
						|
    |> Enum.join(", ")
 | 
						|
  end
 | 
						|
end
 |