# `Chime.Notifier`
[🔗](https://github.com/hattoriai/chime/blob/v0.1.0-dev/lib/chime/notifier.ex#L1)

A kind of notification of the app: who it goes to, by which channels,
and what it says in each. Chime has none of its own; the app writes one
module for each kind it has.

    defmodule MyApp.Notifications.NewComment do
      use Chime.Notifier, category: :comments, group_window: {10, :minute}

      deliver :feed
      deliver :email, wait: {10, :minute}, unless: :read
      deliver :telegram, if: :telegram?

      @impl true
      def recipients(%{comment: comment}),
        do: MyApp.Stories.followers(comment.story_id) -- [comment.author]

      @impl true
      def data(%{comment: comment}) do
        %{"story_id" => comment.story_id, "story" => comment.story.title, "text" => comment.text}
      end

      @impl true
      def actor(%{comment: comment}),
        do: %{"id" => "user:#{comment.author.id}", "name" => comment.author.name}

      @impl true
      def group(%{comment: comment}), do: "story:#{comment.story_id}"

      @impl true
      def feed(data, notification) do
        %{
          title: "#{Chime.actors(notification)} commented on #{data["story"]}",
          body: data["text"],
          url: "/stories/#{data["story_id"]}"
        }
      end

      @impl true
      def email(data, _recipient, notification) do
        Swoosh.Email.new(
          subject: "#{Chime.actors(notification)} commented on #{data["story"]}",
          text_body: data["text"]
        )
      end

      def telegram?(_data, recipient), do: recipient.data.telegram_chat != nil
    end

Then, where the comment is made:

    Chime.notify(MyApp.Notifications.NewComment, %{comment: comment},
      key: "comment:#{comment.id}")

## Options of `use`

  * `:category` - the category of `Chime.Config` it is of, which the
    preferences turn on and off (required).
  * `:kind` - its name in the JSON API and the components
    (`"new_comment"` for `MyApp.Notifications.NewComment`).
  * `:required` - it goes whatever the person's preferences, and its
    emails have no link to unsubscribe (`false`; a category of
    `required: true` makes all its notifiers so).
  * `:notify_actor` - whether the person who did it (`c:actor/1`) is
    notified too, when they are among the recipients (`false`: nobody is
    told of what they did).
  * `:audience_key` - with `c:audience/1`: the field of the query's
    first binding its pages go by (`:id`).
  * `:group_window` - with `c:group/1`: how long a notification stays
    open to the events of its group, from the first (`{1, :hour}`).

## Channels

`deliver/2` declares a channel, in the order they go: `:feed`, the
bell and its list in the app (decided when the notification comes, with
`:if` and `:unless` of the notifier's functions only); `:email`, through the app's mailer; or
one of the app's `:channels` (`Chime.Channel`). Options:

  * `:wait` - how long after the notification it goes: `{10, :minute}`
    (`:second`, `:minute`, `:hour`, `:day`) or a number of seconds.
    What is decided then (the conditions, the preferences) is decided
    at that time.
  * `:unless` - it does not go if: `:read` (the person read it in the
    feed meanwhile), `:seen` (they saw it in the feed), or the name of a
    function of the notifier, `(data, recipient) -> boolean`. A list is
    any of them.
  * `:if` - it goes only if this function of the notifier,
    `(data, recipient) -> boolean`, says so; or `{module, function}`.
  * `:throttle` - at most one by the channel for a group (or, with no
    group, for the notifier) in this time: `{15, :minute}`. One that
    comes sooner waits for the end of it.
  * `:debounce` - with `:wait`: each event that joins the group while
    it waits moves its time to `:wait` after that event, so a quick run
    of events (messages a minute apart) goes as one. `:max_wait` is the
    most it waits from the first (none).
  * `:quiet` - `false` sends it in the person's quiet hours too
    (`:quiet_hours` of `Chime.Config`). A required notification always
    goes.

## What it says

The feed and the emails are made from `c:data/1`, kept on the event as
JSON when the notification comes, and rendered when they are shown: in
the person's language, as the app's words are then, and with the count
of a group ("Ana and 4 others"). Each channel calls its function of
the notifier: `c:feed/2`, `c:email/3`, and, for an app's channel, the
function of its name (`telegram/3`) when there is one.

See the [Notifiers](notifiers.md) guide.

# `feed`

```elixir
@type feed() :: %{
  :title =&gt; String.t(),
  optional(:body) =&gt; String.t() | nil,
  optional(:url) =&gt; String.t() | nil,
  optional(:icon) =&gt; String.t() | nil,
  optional(atom()) =&gt; term()
}
```

What the feed shows of a notification.

# `params`

```elixir
@type params() :: term()
```

The params given to `Chime.notify/3`.

# `act`
*optional* 

```elixir
@callback act(action :: String.t(), data :: map(), Chime.Notification.t()) ::
  :ok | {:ok, String.t()} | {:error, term()}
```

Takes an action of the feed's `:actions` (`Chime.act/3`): with its key,
the data and the notification (`notification.recipient` is who takes
it). `:ok`, or `{:ok, url}` for where the person goes next; an error
leaves the notification as it was.

# `actor`
*optional* 

```elixir
@callback actor(params()) :: map() | nil
```

Who did it: `%{"id" => ..., "name" => ...}`, or nil (nil).

# `audience`
*optional* 

```elixir
@callback audience(data :: map()) :: Ecto.Queryable.t()
```

Everyone it goes to, for many people: a query of the app's (`Ecto.Query`),
from the event's data, instead of `c:recipients/1`. Chime goes through it
in Oban jobs, 1000 people a page, by the key of the query's first
binding (`:audience_key` of `use`, `:id`), and makes each key a person's
id with `c:recipient_id/1`:

    @impl true
    def audience(_data), do: from(u in MyApp.User, where: not is_nil(u.confirmed_at))

    @impl true
    def recipient_id(id), do: "user:#{id}"

The guard (`c:notify?/2`) is not asked of an audience: its people are
the query's.

# `data`
*optional* 

```elixir
@callback data(params()) :: map()
```

What is kept of the params, as JSON (a map of strings, numbers, lists
and maps), for the feed and the channels to render from. By default,
the params themselves, as JSON.

# `email`
*optional* 

```elixir
@callback email(data :: map(), Chime.Recipient.t(), Chime.Notification.t()) :: struct()
```

The email: a `Swoosh.Email` with its subject and body. Chime gives it
its `To:` and, unless the notification is required, the link and the
headers to unsubscribe; its `From:` is the config's unless it has one.

# `feed`
*optional* 

```elixir
@callback feed(data :: map(), Chime.Notification.t()) :: feed()
```

What the feed shows: `:title`, and `:body`, `:url`, `:icon`, and
`:actions`, the buttons of the notification: `[%{key: "accept", label:
"Accept"}, %{key: "decline", label: "Decline"}]`, which `c:act/3`
takes. Once the person took one, `notification.action` is its key.

# `group`
*optional* 

```elixir
@callback group(params()) :: String.t() | nil
```

The group its notifications join: a string, such as `"story:42"`, or
nil. A person's notification of the same group, unread and within the
`:group_window`, takes the new event instead of a new notification: its
count goes up, its actors take the new one, and it comes to the top of
the feed. By default, nil: no groups.

# `notify?`
*optional* 

```elixir
@callback notify?(params(), Chime.Recipient.t()) :: boolean()
```

Whether a person is notified at all: the guard of the app's rules, such
as "may this person read the channel?", checked for each person when
the notification comes, with the params of `Chime.notify/3` and the
`Chime.Recipient` (as the resolver gives them). A person it says no to
gets nothing, by any channel. By default, everyone.

# `preview`
*optional* 

```elixir
@callback preview() :: term() | [{String.t(), term()}]
```

Params to see the notifier with, in the admin's previews
(`Chime.Router.Admin`): a map, as `Chime.notify/3` gets them, or a list
of `{name, params}` for more than one. They are given to `data/1` and
`actor/1`, so they can be plain maps of what those read:

    @impl true
    def preview do
      [
        {"A comment", %{comment: %{story_id: 1, story: %{title: "Tokyo"}, text: "Lovely.", author: %{id: 7, name: "Ana"}}}},
        {"A long one", %{comment: %{story_id: 1, story: %{title: "Tokyo"}, text: String.duplicate("Lovely. ", 40), author: %{id: 7, name: "Ana"}}}}
      ]
    end

# `recipient_id`
*optional* 

```elixir
@callback recipient_id(term()) :: String.t()
```

The id of a person of the audience, from its key (the key as a string).

# `recipients`
*optional* 

```elixir
@callback recipients(params()) :: [term()]
```

Who it goes to: the app's users (turned into recipients by
`c:Chime.Resolver.recipient/1`), `Chime.Recipient`s, or their ids.
`Chime.notify/3` with `:to` does not call it.

# `workspace`
*optional* 

```elixir
@callback workspace(params()) :: String.t() | nil
```

The workspace it happens in: the id of a team, an organisation, a
collective of the app's, as a string, or nil (nil). Its notifications
are of that workspace: a feed, counts and preferences can be of one
workspace (see the [Workspaces](workspaces.md) guide). `Chime.notify/3`
with `:workspace` gives it instead.

# `deliver`
*macro* 

Declares a channel, with its options (see the moduledoc).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
