Current section
Files
Jump to
Current section
Files
lib/surge/events/call_ended.ex
defmodule Surge.Events.CallEnded do
@moduledoc """
The `call.ended` event is delivered whenever a call is completed between a
Surge number you own and another phone number.
"""
alias Surge.Contacts.Contact
@type status :: :busy | :canceled | :completed | :failed | :missed | :no_answer
@type t :: %__MODULE__{
id: String.t() | nil,
contact: Contact.t() | nil,
duration: integer() | nil,
initiated_at: DateTime.t() | nil,
status: status() | nil
}
defstruct [
:id,
:contact,
:duration,
:initiated_at,
:status
]
@doc """
Converts JSON webhook payload to CallEnded struct.
## Examples
iex> data = %{
...> "id" => "call_01jjnn7s0zfx5tdcsxjfy93et2",
...> "contact" => %{
...> "id" => "ctc_01ja88cboqffhswjx8zbak3ykk",
...> "email" => "dom@toretto.family",
...> "first_name" =>: "Dominic",
...> "last_name" => "Toretto",
...> "metadata" =>: {
...> "car" =>: "1970 Dodge Charger R/T"
...> },
...> "phone_number" => "+18015551234"
...> },
...> "duration" => 184,
...> "initiated_at" => "2025-03-31T21:01:37Z",
...> "status" => "completed"
...> }
iex> Surge.Events.CallEnded.from_json(data)
%Surge.Events.CallEnded{
id: "call_01jjnn7s0zfx5tdcsxjfy93et2",
contact: %Surge.Contacts.Contact{
id: "ctc_01ja88cboqffhswjx8zbak3ykk",
email: "dom@toretto.family",
first_name: "Dominic",
last_name: "Toretto",
metadata: %{
"car" => "1970 Dodge Charger R/T"
},
phone_number: "+18015551234"
},
duration: 184,
initiated_at: ~U[2025-03-31 21:01:37Z],
status: :completed
}
"""
@spec from_json(map()) :: t()
def from_json(data) when is_map(data) do
%__MODULE__{
id: data["id"],
contact: parse_contact(data["contact"]),
duration: data["duration"],
initiated_at: parse_datetime(data["initiated_at"]),
status: parse_status(data["status"])
}
end
# Private
@spec parse_contact(map() | nil) :: Contact.t() | nil
defp parse_contact(nil), do: nil
defp parse_contact(data) when is_map(data), do: Contact.from_json(data)
@spec parse_datetime(String.t() | nil) :: DateTime.t() | nil
defp parse_datetime(nil), do: nil
defp parse_datetime(str) when is_binary(str) do
case DateTime.from_iso8601(str) do
{:ok, datetime, _} -> datetime
_ -> nil
end
end
@spec parse_status(String.t() | nil) :: status() | nil
defp parse_status(nil), do: nil
defp parse_status("busy"), do: :busy
defp parse_status("canceled"), do: :canceled
defp parse_status("completed"), do: :completed
defp parse_status("failed"), do: :failed
defp parse_status("missed"), do: :missed
defp parse_status("no_answer"), do: :no_answer
defp parse_status(_), do: nil
end