Skip to main content

lib/jido/chat/telegram/update_envelope.ex

defmodule Jido.Chat.Telegram.UpdateEnvelope do
  @moduledoc """
  Typed Telegram update envelope for extension-level ingest.
  """

  alias Jido.Chat.Wire

  @update_types [
    :message,
    :edited_message,
    :channel_post,
    :edited_channel_post,
    :callback_query,
    :message_reaction,
    :noop,
    :unsupported
  ]

  @schema Zoi.struct(
            __MODULE__,
            %{
              id: Zoi.string(),
              update_id: Zoi.integer() |> Zoi.nullish(),
              update_type: Zoi.enum(@update_types),
              payload: Zoi.any() |> Zoi.nullish(),
              raw: Zoi.map() |> Zoi.default(%{}),
              metadata: Zoi.map() |> Zoi.default(%{})
            },
            coerce: true
          )

  @type t :: unquote(Zoi.type_spec(@schema))

  @enforce_keys Zoi.Struct.enforce_keys(@schema)
  defstruct Zoi.Struct.struct_fields(@schema)

  @doc "Returns the Zoi schema for UpdateEnvelope."
  def schema, do: @schema

  @doc "Creates a Telegram update envelope."
  @spec new(map()) :: t()
  def new(attrs) when is_map(attrs) do
    attrs
    |> Map.put_new(:id, Jido.Chat.ID.generate!())
    |> then(&Jido.Chat.Schema.parse!(__MODULE__, @schema, &1))
  end

  @doc "Serializes update envelope into a plain map with type marker."
  @spec to_map(t()) :: map()
  def to_map(%__MODULE__{} = envelope) do
    envelope
    |> Map.from_struct()
    |> Wire.to_plain()
    |> Map.put("__type__", "telegram_update_envelope")
  end

  @doc "Builds update envelope from serialized map data."
  @spec from_map(map()) :: t()
  def from_map(map) when is_map(map), do: new(map)
end