Skip to content

GimliCZ/EventSub-Websocket-Twitch

Repository files navigation

Twitch EventSub Websocket

NuGet version NuGet downloads .NET 10 License
Issues Stars Forks Last Commit

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.

About

Handles multiple user communications with Twitch Eventsub via websocket

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages