defmodule Jido.Chat.Response do
@moduledoc """
Canonical normalized outbound send/edit result.
"""
@schema Zoi.struct(
__MODULE__,
%{
external_message_id: Zoi.string() |> Zoi.nullish(),
external_room_id: Zoi.any() |> Zoi.nullish(),
timestamp: Zoi.any() |> Zoi.nullish(),
channel_type: Zoi.atom() |> Zoi.nullish(),
status: Zoi.enum([:sent, :edited, :accepted, :failed]) |> Zoi.default(:sent),
raw: Zoi.any() |> Zoi.nullish(),
metadata: Zoi.map() |> Zoi.default(%{}),
message_id: Zoi.any() |> Zoi.nullish(),
chat_id: Zoi.any() |> Zoi.nullish(),
channel_id: Zoi.any() |> Zoi.nullish(),
date: Zoi.any() |> Zoi.nullish()
},
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 Response."
def schema, do: @schema
@doc "Creates a canonical response struct from adapter data."
def new(attrs) when is_map(attrs) do
fetch = fn key ->
Map.get(attrs, key) || Map.get(attrs, Atom.to_string(key))
end
raw_timestamp = fetch.(:timestamp) || fetch.(:date)
external_message_id =
fetch.(:external_message_id) ||
fetch.(:message_id)
external_room_id =
fetch.(:external_room_id) ||
fetch.(:chat_id) ||
fetch.(:channel_id)
channel_type = fetch.(:channel_type)
normalized_timestamp =
case normalize_timestamp(raw_timestamp) do
nil -> raw_timestamp
dt -> dt
end
%{
external_message_id: stringify(external_message_id),
external_room_id: external_room_id,
timestamp: normalized_timestamp,
channel_type: channel_type,
status: fetch.(:status) || :sent,
raw: fetch.(:raw),
metadata: fetch.(:metadata) || %{},
message_id: external_message_id,
chat_id: fetch.(:chat_id) || maybe_chat_id(channel_type, external_room_id),
channel_id: fetch.(:channel_id) || maybe_channel_id(channel_type, external_room_id),
date: fetch.(:date) || raw_timestamp
}
|> then(&Jido.Chat.Schema.parse!(__MODULE__, @schema, &1))
end
defp maybe_chat_id(:telegram, external_room_id), do: external_room_id
defp maybe_chat_id(_, _), do: nil
defp maybe_channel_id(:discord, external_room_id), do: external_room_id
defp maybe_channel_id(_, _), do: nil
defp stringify(nil), do: nil
defp stringify(value) when is_binary(value), do: value
defp stringify(value), do: to_string(value)
defp normalize_timestamp(nil), do: nil
defp normalize_timestamp(%DateTime{} = dt), do: dt
defp normalize_timestamp(value) when is_integer(value) do
case DateTime.from_unix(value) do
{:ok, dt} -> dt
_ -> nil
end
end
defp normalize_timestamp(value) when is_binary(value) do
case DateTime.from_iso8601(value) do
{:ok, dt, _offset} -> dt
_ -> nil
end
end
defp normalize_timestamp(_), do: nil
end