Teac.ConduitManager (teac v1.0.0)

Copy Markdown View Source

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.

Summary

Functions

Returns a specification to start this module under a supervisor.

Returns the conduit ID managed by this process.

Returns the current shard map of shard_id => pid.

Starts a ConduitManager linked to the calling process.

Creates an EventSub subscription routed through this conduit.

Deletes an EventSub subscription by its Twitch subscription ID.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

conduit_id(manager)

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

Returns the conduit ID managed by this process.

shards(manager)

@spec shards(GenServer.server()) :: %{required(non_neg_integer()) => pid()}

Returns the current shard map of shard_id => pid.

start_link(client, opts \\ [])

@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(manager, opts)

@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(manager, subscription_id)

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

Deletes an EventSub subscription by its Twitch subscription ID.