2.2 KiB · text History 4b6fe3b
defmodule OhMyExOhIrcd.Images.ChannelConfig do
  @moduledoc """
  Per-channel ImgServ configuration. One row per registered channel set up
  for image hosting. `joined` tracks whether ImgServ is currently in the
  channel; `allowlist` holds downcased `who` display names;
  `retention_days` is an optional per-channel override of the server's
  default GC window (NULL = use the server default).
  """

  use Ecto.Schema
  import Ecto.Changeset

  alias OhMyExOhIrcd.Channels.ChannelRecord

  @default_format "%who% posted an image: %url% %title%"

  schema "image_channel_configs" do
    field :enabled, :boolean, default: true
    field :joined, :boolean, default: false
    field :format, :string, default: @default_format
    field :rate_limit_seconds, :integer, default: 30
    field :restrict_to_allowlist, :boolean, default: false
    field :allowlist, {:array, :string}, default: []
    field :retention_days, :integer

    belongs_to :channel, ChannelRecord

    timestamps(type: :utc_datetime)
  end

  @doc "Default announcement template."
  def default_format, do: @default_format

  def changeset(record, attrs) do
    record
    |> cast(attrs, [
      :channel_id,
      :enabled,
      :joined,
      :format,
      :rate_limit_seconds,
      :restrict_to_allowlist,
      :allowlist,
      :retention_days
    ])
    |> validate_required([:channel_id])
    |> validate_length(:format, min: 1, max: 390)
    |> validate_number(:rate_limit_seconds,
      greater_than_or_equal_to: 0,
      less_than_or_equal_to: 86_400
    )
    |> validate_retention_days()
    |> sanitize_format()
    |> unique_constraint(:channel_id)
    |> assoc_constraint(:channel)
  end

  defp validate_retention_days(changeset) do
    case get_field(changeset, :retention_days) do
      nil ->
        changeset

      _ ->
        validate_number(changeset, :retention_days, greater_than: 0, less_than_or_equal_to: 3650)
    end
  end

  # Strip control chars from the template so an op can't smuggle an IRC
  # line break into announcements.
  defp sanitize_format(changeset) do
    update_change(changeset, :format, fn fmt ->
      fmt
      |> String.replace(~r/[\x00-\x1f]/, "")
      |> String.trim()
    end)
  end
end