Current section

Files

Jump to
pigeon lib pigeon notification.ex
Raw

lib/pigeon/notification.ex

defmodule Pigeon.Notification do
@moduledoc """
Contains shared functions for GCM and APNS.
"""
require Logger
def json_payload(payload) do
response = Poison.encode(payload)
case response do
{:ok, result} ->
result
{:error, error} ->
Logger.error error
end
end
end
defmodule Pigeon.APNS.Notification do
@moduledoc """
Defines APNS notification struct and convenience constructor functions.
"""
defstruct device_token: nil, payload: %{"aps" => %{}}, expiration: nil, topic: nil, id: nil
@type t :: %__MODULE__{
device_token: String.t,
expiration: String.t,
id: String.t,
payload: %{},
topic: String.t
}
@doc """
Returns an `APNS.Notification` struct with given message, device token, and
topic (optional).
Push payload is constructed in the form of `%{"aps" => %{"alert" => msg}}`
## Examples
iex> Pigeon.APNS.Notification.new("push message", "device token")
%Pigeon.APNS.Notification{
device_token: "device token",
expiration: nil,
id: nil,
payload: %{"aps" => %{"alert" => "push message"}},
topic: nil
}
"""
@spec new(String.t, String.t, String.t | nil) :: t
def new(msg, token, topic \\ nil) do
%Pigeon.APNS.Notification{
device_token: token,
payload: %{"aps" => %{"alert" => msg}},
topic: topic
}
end
@doc """
Returns an `APNS.Notification` struct with given message, device token,
topic, and message ID.
Push payload is constructed in the form of `%{"aps" => %{"alert" => msg}}`
## Examples
iex> Pigeon.APNS.Notification.new("push message", "device token", "topic", "id_1234")
%Pigeon.APNS.Notification{
device_token: "device token",
expiration: nil,
id: "id_1234",
payload: %{"aps" => %{"alert" => "push message"}},
topic: "topic"
}
"""
@spec new(String.t, String.t, String.t, String.t) :: t
def new(msg, token, topic, id) do
%Pigeon.APNS.Notification{
device_token: token,
id: id,
payload: %{"aps" => %{"alert" => msg}},
topic: topic
}
end
@doc """
Updates `"alert"` key in push payload.
This is the alert message displayed on the device.
## Examples
iex> Pigeon.APNS.Notification.put_alert(%Pigeon.APNS.Notification{}, "push message")
%Pigeon.APNS.Notification{
device_token: nil,
expiration: nil,
id: nil,
payload: %{"aps" => %{"alert" => "push message"}},
topic: nil
}
"""
@spec put_alert(t, String.t) :: t
def put_alert(notification, alert), do: update_payload(notification, "alert", alert)
@doc """
Updates `"badge"` key in push payload.
This is the badge number displayed on the application.
## Examples
iex> Pigeon.APNS.Notification.put_badge(%Pigeon.APNS.Notification{}, 5)
%Pigeon.APNS.Notification{
device_token: nil,
expiration: nil,
id: nil,
payload: %{"aps" => %{"badge" => 5}},
topic: nil
}
"""
@spec put_badge(t, integer) :: t
def put_badge(notification, badge), do: update_payload(notification, "badge", badge)
@doc """
Updates `"sound"` key in push payload.
Used for custom notification sounds. Value should
be the name of the custom sound file in the application's binary.
## Examples
iex> Pigeon.APNS.Notification.put_sound(%Pigeon.APNS.Notification{}, "custom.aiff")
%Pigeon.APNS.Notification{
device_token: nil,
expiration: nil,
id: nil,
payload: %{"aps" => %{"sound" => "custom.aiff"}},
topic: nil
}
"""
@spec put_sound(t, String.t) :: t
def put_sound(notification, sound), do: update_payload(notification, "sound", sound)
@doc """
Sets `"content-available"` flag in push payload.
Used for silent notifications. When set, ensure `alert`, `badge`, and `sound` keys
are not configured.
## Examples
iex> Pigeon.APNS.Notification.put_content_available(%Pigeon.APNS.Notification{})
%Pigeon.APNS.Notification{
device_token: nil,
expiration: nil,
id: nil,
payload: %{"aps" => %{"content-available" => 1}},
topic: nil
}
"""
@spec put_content_available(t) :: t
def put_content_available(notification), do: update_payload(notification, "content-available", 1)
@doc """
Updates `"category"` key in push payload.
## Examples
iex> Pigeon.APNS.Notification.put_category(%Pigeon.APNS.Notification{}, "category")
%Pigeon.APNS.Notification{
device_token: nil,
expiration: nil,
id: nil,
payload: %{"aps" => %{"category" => "category"}},
topic: nil
}
"""
@spec put_category(t, String.t) :: t
def put_category(notification, category), do: update_payload(notification, "category", category)
@doc """
Sets `"mutable-content"` flag in push payload.
Used for notification service extensions (such as displaying custom media).
## Examples
iex> Pigeon.APNS.Notification.put_mutable_content(%Pigeon.APNS.Notification{})
%Pigeon.APNS.Notification{
device_token: nil,
expiration: nil,
id: nil,
payload: %{"aps" => %{"mutable-content" => 1}},
topic: nil
}
"""
@spec put_mutable_content(t) :: t
def put_mutable_content(notification), do: update_payload(notification, "mutable-content", 1)
defp update_payload(notification, key, value) do
new_aps =
notification.payload
|> Map.get("aps")
|> Map.put(key, value)
new_payload = notification.payload |> Map.put("aps", new_aps)
%{notification | payload: new_payload}
end
@doc """
Puts custom data in push payload.
## Examples
iex> n = Pigeon.APNS.Notification.new("test message", "device token")
iex> Pigeon.APNS.Notification.put_custom(n, %{"custom-key" => 1234})
%Pigeon.APNS.Notification{
device_token: "device token",
expiration: nil,
id: nil,
payload: %{"aps" => %{"alert" => "test message"}, "custom-key" => 1234},
topic: nil
}
"""
@spec put_custom(t, String.t) :: t
def put_custom(notification, data) do
new_payload = Map.merge(notification.payload, data)
%{notification | payload: new_payload}
end
end
defmodule Pigeon.GCM.Notification do
@moduledoc """
Defines GCM notification struct and convenience constructor functions.
"""
defstruct registration_id: nil, payload: %{}, message_id: nil, updated_registration_id: nil
@type t :: %__MODULE__{
message_id: String.t,
payload: %{},
registration_id: String.t | [String.t],
updated_registration_id: String.t
}
@doc """
Creates `GCM.Notification` struct with device registration IDs and optional
notification and data payloads.
## Examples
iex> Pigeon.GCM.Notification.new("reg ID")
%Pigeon.GCM.Notification{
message_id: nil,
payload: %{},
registration_id: "reg ID",
updated_registration_id: nil
}
iex> Pigeon.GCM.Notification.new("reg ID", %{"body" => "test message"})
%Pigeon.GCM.Notification{
message_id: nil,
payload: %{"notification" => %{"body" => "test message"}},
registration_id: "reg ID",
updated_registration_id: nil
}
iex> Pigeon.GCM.Notification.new("reg ID", %{"body" => "test message"}, %{"key" => "value"})
%Pigeon.GCM.Notification{
message_id: nil,
payload: %{
"data" => %{"key" => "value"},
"notification" => %{"body" => "test message"}
},
registration_id: "reg ID",
updated_registration_id: nil
}
"""
def new(registration_ids, notification \\ %{}, data \\ %{})
def new(registration_ids, notification, data) do
%Pigeon.GCM.Notification{registration_id: registration_ids}
|> put_notification(notification)
|> put_data(data)
end
@doc """
Updates `"data"` key in push payload.
## Examples
iex> Pigeon.GCM.Notification.put_data(%Pigeon.GCM.Notification{}, %{"key" => 1234})
%Pigeon.GCM.Notification{
message_id: nil,
payload: %{"data" => %{"key" => 1234}},
registration_id: nil,
updated_registration_id: nil
}
"""
def put_data(n, data), do: update_payload(n, "data", data)
@doc """
Updates `"notification"` key in push payload.
## Examples
iex> Pigeon.GCM.Notification.put_notification(%Pigeon.GCM.Notification{},
...> %{"body" => "message"})
%Pigeon.GCM.Notification{
message_id: nil,
payload: %{"notification" => %{"body" => "message"}},
registration_id: nil,
updated_registration_id: nil
}
"""
def put_notification(n, notification), do: update_payload(n, "notification", notification)
defp update_payload(notification, _key, value) when value == %{}, do: notification
defp update_payload(notification, key, value) do
payload =
notification.payload
|> Map.put(key, value)
%{notification | payload: payload}
end
end
defmodule Pigeon.ADM.Notification do
@moduledoc """
Defines Amazon ADM notification struct and convenience constructor functions.
"""
defstruct registration_id: nil, payload: %{}, updated_registration_id: nil,
consolidation_key: nil, expires_after: 604800, md5: nil
@type t :: %__MODULE__{
consolidation_key: String.t,
expires_after: integer,
md5: binary,
payload: %{},
registration_id: String.t,
updated_registration_id: String.t
}
@doc """
Creates `ADM.Notification` struct with device registration ID and optional data payload.
## Examples
iex> Pigeon.ADM.Notification.new("reg ID")
%Pigeon.ADM.Notification{
consolidation_key: nil,
md5: "1B2M2Y8AsgTpgAmY7PhCfg==",
payload: %{"data" => %{}},
registration_id: "reg ID",
updated_registration_id: nil
}
iex> Pigeon.ADM.Notification.new("reg ID", %{"message" => "your message"})
%Pigeon.ADM.Notification{
consolidation_key: nil,
md5: "qzF+HgArKZjJrpfcTbiFxg==",
payload: %{
"data" => %{"message" => "your message"}
},
registration_id: "reg ID",
updated_registration_id: nil
}
"""
def new(registration_id, data \\ %{})
def new(registration_id, data) do
%Pigeon.ADM.Notification{registration_id: registration_id}
|> put_data(data)
end
@doc """
Updates `"data"` key on push payload and calculates `md5` hash.
## Examples
iex> n = %Pigeon.ADM.Notification{}
iex> Pigeon.ADM.Notification.put_data(n, %{"message" => "your message"})
%Pigeon.ADM.Notification{
consolidation_key: nil,
md5: "qzF+HgArKZjJrpfcTbiFxg==",
payload: %{
"data" => %{"message" => "your message"}
},
registration_id: nil,
updated_registration_id: nil
}
"""
def put_data(n, data) do
n
|> update_payload("data", ensure_strings(data))
|> calculate_md5
end
defp update_payload(notification, key, value) when value == %{} and key != "data",
do: notification
defp update_payload(notification, key, value) do
payload =
notification.payload
|> Map.put(key, value)
%{notification | payload: payload}
end
@doc """
ADM requires that "data" keys and values are all strings.
"""
def ensure_strings(data) do
data
|> Enum.map(fn {key, value} -> {"#{key}", "#{value}"} end)
|> Enum.into(%{})
end
@doc """
Calculates md5 hash of notification data payload.
"""
def calculate_md5(%{payload: %{"data" => data}} = notification) when is_map(data) do
concat =
data
|> Map.keys
|> Enum.sort
|> Enum.map(fn key -> "#{key}:#{data[key]}" end)
|> Enum.join(",")
md5 = :md5 |> :crypto.hash(concat) |> Base.encode64
%{notification | md5: md5}
end
def calculate_md5(notification), do: notification
end