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
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec conduit_id(GenServer.server()) :: String.t()
Returns the conduit ID managed by this process.
@spec shards(GenServer.server()) :: %{required(non_neg_integer()) => pid()}
Returns the current shard map of shard_id => pid.
@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 implementingTeac.WssClient.Handler.:handler_state- optional. Initial state passed to handler callbacks. Defaults tonil.:conduit_id- optional. An existing conduit ID to adopt. If omitted, a new conduit is created withshard_countshards.:name- optional. Registers the process under this name.
@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.
@spec unsubscribe(GenServer.server(), String.t()) :: {:ok, nil} | {:error, term()}
Deletes an EventSub subscription by its Twitch subscription ID.