# `ExWapp.Session.Outbound.Direct`
[🔗](https://github.com/elchemista/ex_wapp/blob/v0.1.5/lib/ex_wapp/session/outbound/direct.ex#L1)

Direct-message and self-chat outbound orchestration decisions.

The session worker provides callbacks for USync and prekey IQ effects and
remains responsible for encryption, persistence, and transport sends. This
module owns direct session preparation, payload construction, fanout
resolution, and final direct message-node composition.

# `discover_fun`

```elixir
@type discover_fun() :: (String.t(), String.t(), non_neg_integer() | nil -&gt;
                     discovery_result())
```

# `discovery_result`

```elixir
@type discovery_result() ::
  {[String.t()], [String.t()], [String.t()], [String.t()], [String.t()],
   [String.t()]}
```

# `payloads`

```elixir
@type payloads() :: %{recipient: binary(), own: binary(), raw_size: non_neg_integer()}
```

# `target_hints`

```elixir
@type target_hints() :: %{
  recipient_devices: [String.t()],
  recipient_cached_devices: [String.t()],
  own_devices: [String.t()],
  own_cached_devices: [String.t()],
  own_identity_jid: String.t(),
  all_usync_devices: [String.t()]
}
```

# `build_encrypted_message_node`

```elixir
@spec build_encrypted_message_node(
  String.t(),
  String.t(),
  [ExWapp.Binary.Node.t()],
  term(),
  boolean()
) :: ExWapp.Binary.Node.t()
```

Builds the final multi-device direct message node.

# `build_payloads`

```elixir
@spec build_payloads(WAWebProtobufsE2E.Message.t(), String.t(), keyword()) ::
  {:ok, payloads()} | {:error, {:proto_encode, term()}}
```

Builds padded recipient and own-device payloads for a direct send.

# `message_id`

```elixir
@spec message_id(keyword(), String.t()) :: String.t()
```

Returns the retry message ID or generates a new outbound ID.

# `prepare_sessions`

```elixir
@spec prepare_sessions(
  map(),
  String.t(),
  non_neg_integer(),
  discover_fun(),
  ExWapp.Session.Outbound.Prekeys.fetch_many_fun(),
  ExWapp.Session.Outbound.Prekeys.fetch_one_fun(),
  keyword()
) ::
  {:ok, ExWapp.Signal.SessionStore.t(), map(), target_hints()}
  | {:error, term()}
```

Prepares Signal sessions and target hints for a direct or self-chat send.

Network effects are supplied as callbacks so the worker keeps ownership of
IQ sends, receives, and timeouts.

# `resolve_targets`

```elixir
@spec resolve_targets(
  term(),
  ExWapp.Signal.SessionStore.t(),
  String.t(),
  map(),
  map(),
  keyword()
) :: {[map()], [String.t()]}
```

Resolves direct recipient and own-device fanout targets.

Stale cached device entries rejected by the resolution are evicted.

# `validate_participant_family`

```elixir
@spec validate_participant_family(String.t(), [ExWapp.Binary.Node.t()]) ::
  :ok | {:error, {:mixed_addressing_family, non_neg_integer()}}
```

Verifies that every encrypted participant uses the destination JID family.

This is the final pre-send invariant. Fanout resolution performs the mapping;
this check prevents a later regression from putting a PN participant into a
LID message (or the reverse).

---

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