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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Twitch.EventSub.Websocket" Version="3.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Twitch.EventSub.Websocket" Version="3.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Twitch.EventSub.Websocket" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Twitch.EventSub.Websocket --version 3.0.2
                    
#r "nuget: Twitch.EventSub.Websocket, 3.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Twitch.EventSub.Websocket@3.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Twitch.EventSub.Websocket&version=3.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Twitch.EventSub.Websocket&version=3.0.2
                    
Install as a Cake Tool

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 HttpClient instances with standard resilience pipelines (retry, circuit breaker, timeout) and telemetry enrichment
  • TwitchApi and TwitchApiConduit as singletons
  • IEventSubClient as 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 OnRefreshTokenAsync to 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

Alt text

License

This project is available under the MIT license. See the LICENSE file for more info.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed