# `Teac.ConduitManager`
[🔗](https://codeberg.org/FullStacking/teac)

Manages a Twitch EventSub conduit and its WebSocket shard pool.

A conduit lets you fan out EventSub subscriptions across multiple WebSocket
connections (shards). Subscriptions are attached to the conduit itself — not
to individual sessions — so they survive shard reconnects automatically.

## Usage

    # At application start (or in a supervisor):
    {:ok, manager} = Teac.ConduitManager.start_link(client,
      shard_count: 3,
      handler: MyApp.TwitchHandler,
      handler_state: %{},
      name: MyApp.ConduitManager
    )

    # Subscribe to events for a channel (idempotent across reconnects):
    {:ok, _} = Teac.ConduitManager.subscribe(MyApp.ConduitManager,
      type: "channel.chat.message",
      version: "1",
      condition: %{"broadcaster_user_id" => "123", "user_id" => "123"}
    )

    # Clean up a subscription by its Twitch subscription ID:
    Teac.ConduitManager.unsubscribe(MyApp.ConduitManager, subscription_id)

## Handler

Implement `Teac.WssClient.Handler` — the same behaviour used for direct
WebSocket connections. Events arriving on any shard are delivered to your
handler transparently.

## Supervision

Include in a supervisor with `:permanent` restart (the default):

    children = [
      {Teac.ConduitManager, {client, shard_count: 3, handler: MyApp.Handler}}
    ]

## Reconnect behaviour

When Twitch issues a `session_reconnect` for a shard, the manager starts a
new `WssClient` at the provided reconnect URL, assigns it to the same shard,
and closes the old connection once the new session is live. Subscriptions
are unaffected — they stay on the conduit throughout.

## Subscription cost budget

Twitch imposes a point budget on EventSub subscriptions — this is an internal
quota system, **not a billing charge**. Each app access token has a default
budget of 10,000 points. Most subscription types cost 1 point; some cost more
(check the Twitch EventSub docs for per-type costs). Conduit subscriptions
consume from this budget just like WebSocket subscriptions.

When you call `Teac.Api.EventSub.Subscriptions.get/2`, the response includes
`total_cost` (points consumed) and `max_total_cost` (your budget). A `post/2`
that would exceed the budget returns a 422 error from Twitch.

If you need to monitor usage, poll `Teac.Api.EventSub.Subscriptions.get/2`
and inspect those two fields.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `conduit_id`

```elixir
@spec conduit_id(GenServer.server()) :: String.t()
```

Returns the conduit ID managed by this process.

# `shards`

```elixir
@spec shards(GenServer.server()) :: %{required(non_neg_integer()) =&gt; pid()}
```

Returns the current shard map of `shard_id => pid`.

# `start_link`

```elixir
@spec start_link(
  Teac.Client.t(),
  keyword()
) :: GenServer.on_start()
```

Starts a `ConduitManager` linked to the calling process.

## Options

  * `:shard_count` - required. Number of WebSocket shards to maintain.
  * `:handler` - required. A module implementing `Teac.WssClient.Handler`.
  * `:handler_state` - optional. Initial state passed to handler callbacks. Defaults to `nil`.
  * `:conduit_id` - optional. An existing conduit ID to adopt. If omitted, a new conduit
    is created with `shard_count` shards.
  * `:name` - optional. Registers the process under this name.

# `subscribe`

```elixir
@spec subscribe(
  GenServer.server(),
  keyword()
) :: {:ok, [map()]} | {:error, term()}
```

Creates an EventSub subscription routed through this conduit.

Unlike `Teac.WssClient.subscribe/2`, this creates a durable REST subscription
that persists across shard reconnects.

## Options

  * `:type` - required. Subscription type, e.g. `"channel.follow"`.
  * `:version` - required. Subscription version, e.g. `"2"`.
  * `:condition` - required. Condition map for the subscription type.

# `unsubscribe`

```elixir
@spec unsubscribe(GenServer.server(), String.t()) :: {:ok, nil} | {:error, term()}
```

Deletes an EventSub subscription by its Twitch subscription ID.

---

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