Twitch.EventSub.Websocket
3.0.2
dotnet add package Twitch.EventSub.Websocket --version 3.0.2
NuGet\Install-Package Twitch.EventSub.Websocket -Version 3.0.2
<PackageReference Include="Twitch.EventSub.Websocket" Version="3.0.2" />
<PackageVersion Include="Twitch.EventSub.Websocket" Version="3.0.2" />
<PackageReference Include="Twitch.EventSub.Websocket" />
paket add Twitch.EventSub.Websocket --version 3.0.2
#r "nuget: Twitch.EventSub.Websocket, 3.0.2"
#:package Twitch.EventSub.Websocket@3.0.2
#addin nuget:?package=Twitch.EventSub.Websocket&version=3.0.2
#tool nuget:?package=Twitch.EventSub.Websocket&version=3.0.2
Twitch EventSub Websocket
<p align="center"> <a href="https://www.nuget.org/packages/Twitch.EventSub.Websocket/" target="_blank"> <img src="https://img.shields.io/nuget/v/Twitch.EventSub.Websocket.svg?label=NuGet%20v" alt="NuGet version" style="max-height:300px;" /> <img src="https://img.shields.io/nuget/dt/Twitch.EventSub.Websocket.svg?label=Downloads" alt="NuGet downloads" style="max-height:300px;" /> </a> <img src="https://img.shields.io/badge/Platform-.NET%2010-orange.svg" style="max-height: 300px;" alt=".NET 10" /> <img src="https://img.shields.io/github/license/GimliCZ/TwitchEventSub_Websocket" alt="License" /> <br /> <img src="https://img.shields.io/github/issues/GimliCZ/TwitchEventSub_Websocket" alt="Issues" /> <img src="https://img.shields.io/github/stars/GimliCZ/TwitchEventSub_Websocket" alt="Stars" /> <img src="https://img.shields.io/github/forks/GimliCZ/TwitchEventSub_Websocket" alt="Forks" /> <img src="https://img.shields.io/github/last-commit/GimliCZ/TwitchEventSub_Websocket" alt="Last Commit" /> </p>
About
- Handles multiple user communications with Twitch EventSub via websocket
- For more information on Twitch EventSub, refer to the Twitch EventSub Documentation.
Migrating from v2 to v3
Version 3.0.0 introduces Dependency Injection as the primary setup path and replaces static API classes with injectable singletons. The following breaking changes require action.
1. Constructor — EventSubClient no longer accepts clientId directly
// v2
var client = new EventSubClient("your-client-id", logger);
// v3 — use DI (see Setup below), or supply options manually
var client = new EventSubClient(
Options.Create(new EventSubClientOptions { ClientId = "your-client-id" }),
logger,
twitchApi);
2. TwitchApi and TwitchApiConduit are no longer static
If you called these classes directly:
// v2
await TwitchApi.SubscribeAsync(...);
await TwitchApiConduit.ConduitCreatorAsync(...);
// v3 — resolve from DI or construct with IHttpClientFactory
var api = new TwitchApi(httpClientFactory);
await api.SubscribeAsync(...);
In practice, you should not need to call these directly — IEventSubClient covers all normal usage.
3. DI registration replaces manual construction
// v2
services.AddSingleton<IEventSubClient>(new EventSubClient("client-id", logger));
// v3
services.AddTwitchEventSub(options => options.ClientId = "your-client-id");
Setup (v3)
Quick start — inline ClientId
// Program.cs / Startup.cs
services.AddTwitchEventSub(options =>
{
options.ClientId = "your-client-id";
});
This registers:
- Two named
HttpClientinstances with standard resilience pipelines (retry, circuit breaker, timeout) and telemetry enrichment TwitchApiandTwitchApiConduitas singletonsIEventSubClientas a singleton- Logging if not already registered
Reading ClientId from configuration / environment variables
Use the IServiceProvider overload to resolve IConfiguration or any other DI service when configuring options:
services.AddTwitchEventSub((sp, options) =>
{
var config = sp.GetRequiredService<IConfiguration>();
options.ClientId = config["Twitch:ClientId"]
?? Environment.GetEnvironmentVariable("TWITCH_CLIENT_ID")
?? throw new InvalidOperationException("Twitch ClientId is not configured.");
});
Implementation
- Client Id is the identifier of your Twitch application
- User Id is the identifier of a Twitch user
- AccessToken is a bearer token obtained for the user
IEventSubClient is the primary interface — inject it wherever you need it.
SETUP
public async Task<bool> SetupAsync(string userId)
{
var listOfSubs = new List<SubscriptionType>
{
SubscriptionType.ChannelFollow
};
_listOfSubs = listOfSubs;
var resultAdd = await _eventSubClient.AddUserAsync(
userId,
GetApiToken(),
_listOfSubs,
allowRecovery: true).ConfigureAwait(false);
if (resultAdd)
{
SetupEvents(userId);
}
return resultAdd;
}
EVENT SUBSCRIPTIONS
private void SetupEvents(string userId)
{
var provider = _eventSubClient[userId];
if (provider == null)
{
_logger.LogError("EventSub Provider returned null for user {UserId}", userId);
return;
}
provider.OnRefreshTokenAsync -= EventSubClientOnRefreshTokenAsync;
provider.OnRefreshTokenAsync += EventSubClientOnRefreshTokenAsync;
provider.OnFollowEventAsync -= EventSubClientOnFollowEventAsync;
provider.OnFollowEventAsync += EventSubClientOnFollowEventAsync;
provider.OnUnexpectedConnectionTermination -= EventSubClientOnUnexpectedConnectionTermination;
provider.OnUnexpectedConnectionTermination += EventSubClientOnUnexpectedConnectionTermination;
#if DEBUG
provider.OnRawMessageAsync -= EventSubClientOnRawMessageAsync;
provider.OnRawMessageAsync += EventSubClientOnRawMessageAsync;
#endif
}
START
await _eventSubClient.StartAsync(userId).ConfigureAwait(false);
STOP
await _eventSubClient.StopAsync(userId).ConfigureAwait(false);
AUTHORIZATION
- EventSub does not provide token refresh capabilities — you must implement your own.
- Subscribe to
OnRefreshTokenAsyncto be notified when a token refresh is needed.
provider.OnRefreshTokenAsync -= EventSubClientOnRefreshTokenAsync;
provider.OnRefreshTokenAsync += EventSubClientOnRefreshTokenAsync;
private async Task EventSubClientOnRefreshTokenAsync(object sender, RefreshRequestArgs e)
{
_logger.LogInformation("EventSub requesting token refresh for user {UserId}", e.UserId);
_eventSubClient.UpdateUser(
e.UserId,
await GetNewAccessTokenAsync(),
_listOfSubs);
}
RECOVERY
Listen to IsConnected and OnUnexpectedConnectionTermination to detect failures and recover.
private async void RecoveryRoutineAsync(string userId)
{
try
{
if (_eventSubClient.IsConnected(userId))
{
_logger.LogDebug("EventSubClient is already connected, skipping recovery");
return;
}
var provider = _eventSubClient[userId];
if (provider != null)
{
provider.OnRefreshTokenAsync -= EventSubClientOnRefreshTokenAsync;
provider.OnFollowEventAsync -= EventSubClientOnFollowEventAsync;
provider.OnUnexpectedConnectionTermination -= EventSubClientOnUnexpectedConnectionTermination;
provider.OnRawMessageAsync -= EventSubClientOnRawMessageAsync;
}
var deleted = await _eventSubClient.DeleteUserAsync(userId);
if (!deleted)
{
_logger.LogWarning("EventSub user was not gracefully terminated during recovery");
}
await SetupAsync(userId).ConfigureAwait(false);
await _eventSubClient.StartAsync(userId).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "EventSub recovery routine failed");
}
}
STATE DIAGRAM
License
This project is available under the MIT license. See the LICENSE file for more info.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Http.Resilience (>= 10.4.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.4)
- Microsoft.VisualStudio.Threading (>= 17.14.15)
- Newtonsoft.Json (>= 13.0.4)
- Stateless (>= 5.20.0)
- Websocket.Client (>= 5.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.2 | 149 | 3/31/2026 |
| 3.0.1 | 90 | 3/31/2026 |
| 2.2.7 | 88 | 3/22/2026 |
| 2.2.6 | 264 | 2/2/2026 |
| 2.2.4 | 293 | 12/18/2025 |
| 2.2.3 | 237 | 12/13/2025 |
| 2.2.2 | 328 | 10/1/2025 |
| 2.2.1 | 311 | 7/14/2025 |
| 2.2.0 | 407 | 6/12/2025 |
| 2.1.2 | 819 | 10/5/2024 |
| 2.1.1-Stable | 193 | 9/14/2024 |
| 2.1.0 | 229 | 8/16/2024 |
| 2.0.6 | 217 | 7/21/2024 |
| 2.0.5 | 192 | 7/15/2024 |
| 2.0.4 | 234 | 7/15/2024 |
| 2.0.3 | 196 | 7/15/2024 |
| 2.0.2 | 198 | 7/14/2024 |
| 2.0.1 | 197 | 7/14/2024 |
| 2.0.0 | 260 | 7/7/2024 |
| 1.2.3 | 282 | 4/29/2024 |