Packages
phoenix_kit
1.7.220
1.7.220
1.7.219
1.7.218
1.7.217
1.7.216
1.7.215
1.7.214
1.7.213
1.7.212
1.7.211
1.7.210
1.7.209
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit/notifications/channels.ex
defmodule PhoenixKit.Notifications.Channels do
@moduledoc """
Registry of notification delivery channels.
`list/0` returns core channels first, then channels contributed by feature
modules via `PhoenixKit.Module`'s `notification_channels/0` — the same
core-first, dedupe-by-key, rescue-guarded merge that
`PhoenixKit.Notifications.Types.list/0` uses for types. A module channel whose
`key/0` collides with a core (or already-seen) key is dropped with a warning,
so core always wins.
Every entry is a module implementing `PhoenixKit.Notifications.Channel`.
"""
require Logger
alias PhoenixKit.ModuleRegistry
# Core channels ship here. Keep alphabetical-by-key for stable UI ordering.
@core_channels [
PhoenixKit.Notifications.Channels.Email,
PhoenixKit.Notifications.Channels.Telegram
]
@doc """
All registered channel modules — core first, then valid module-contributed
ones, with duplicate keys rejected (first claim wins).
"""
@spec list() :: [module()]
def list do
Enum.reduce(@core_channels ++ external_channels(), {[], MapSet.new()}, fn mod, {acc, seen} ->
case safe_key(mod) do
nil ->
{acc, seen}
key ->
if MapSet.member?(seen, key) do
Logger.warning(
"[Notifications.Channels] duplicate channel key #{inspect(key)} from #{inspect(mod)} ignored"
)
{acc, seen}
else
{[mod | acc], MapSet.put(seen, key)}
end
end
end)
|> elem(0)
|> Enum.reverse()
end
@doc "The channel module for `key`, or `nil`."
@spec get(String.t()) :: module() | nil
def get(key) when is_binary(key) do
Enum.find(list(), fn mod -> safe_key(mod) == key end)
end
@doc "All channel keys, in `list/0` order."
@spec keys() :: [String.t()]
def keys, do: Enum.map(list(), & &1.key())
# Collect `notification_channels/0` from every registered module, rescue-guarded
# per module so one bad callback can't take down the whole registry.
defp external_channels do
ModuleRegistry.all_modules()
|> Enum.flat_map(fn mod ->
# `Code.ensure_loaded?/1` first — `function_exported?/3` answers false for
# a module that simply hasn't been loaded yet (the norm under a release's
# lazy loading), which would silently drop that module's channels. Mirrors
# `PhoenixKit.Notifications.Types.external_types/0`.
if Code.ensure_loaded?(mod) and function_exported?(mod, :notification_channels, 0) do
try do
mod.notification_channels() |> List.wrap()
rescue
e ->
Logger.warning(
"[Notifications.Channels] #{inspect(mod)}.notification_channels/0 failed: #{Exception.message(e)}"
)
[]
end
else
[]
end
end)
end
# A channel module must load and answer key/0 with a binary, or it's skipped.
defp safe_key(mod) do
if Code.ensure_loaded?(mod) and function_exported?(mod, :key, 0) do
case mod.key() do
key when is_binary(key) and key != "" -> key
_ -> nil
end
end
rescue
_ -> nil
end
end