diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml index 257b3ad..c759a86 100644 --- a/.github/workflows/preview-release.yml +++ b/.github/workflows/preview-release.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: benjlevesque/short-sha@v2.2 + - uses: benjlevesque/short-sha@v3.0 id: short-sha - name: Setup .NET uses: actions/setup-dotnet@v3 @@ -22,6 +22,6 @@ jobs: - name: Build TwitchLib.EventSub.Websockets run: dotnet build -c Release --no-restore - name: Pack TwitchLib.EventSub.Websockets - run: dotnet pack ./TwitchLib.EventSub.Websockets/TwitchLib.EventSub.Websockets.csproj -v normal -c Release -o nugets --no-build --version-suffix "preview-${{ steps.short-sha.outputs.sha }}" + run: dotnet pack ./TwitchLib.EventSub.Websockets/TwitchLib.EventSub.Websockets.csproj -v normal -c Release -o nugets --no-build --version-suffix "preview.${{ github.run_number }}.${{ steps.short-sha.outputs.sha }}" - name: Push to Nuget run: dotnet nuget push "./nugets/*.nupkg" -k ${{ secrets.API_NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json diff --git a/README.md b/README.md index 151f784..49a336a 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ You can also find a console app example for .NET 8 and for .NET Framework 4.8 in | NuGet | | [![TwitchLib.EventSub.Websockets][1]][2] | | :--------------- | ----: | :--------------------------------------------------------------------------- | -| Package Manager | `PM>` | `Install-Package TwitchLib.EventSub.Websockets -Version 0.5.0` | -| .NET CLI | `>` | `dotnet add package TwitchLib.EventSub.Websockets --version 0.5.0` | -| PackageReference | | `` | -| Paket CLI | `>` | `paket add TwitchLib.EventSub.Websockets --version 0.5.0` | +| Package Manager | `PM>` | `Install-Package TwitchLib.EventSub.Websockets -Version 0.6.0` | +| .NET CLI | `>` | `dotnet add package TwitchLib.EventSub.Websockets --version 0.6.0` | +| PackageReference | | `` | +| Paket CLI | `>` | `paket add TwitchLib.EventSub.Websockets --version 0.6.0` | [1]: https://img.shields.io/nuget/v/TwitchLib.EventSub.Websockets.svg?label=TwitchLib.EventSub.Websockets [2]: https://www.nuget.org/packages/TwitchLib.EventSub.Websockets diff --git a/TwitchLib.EventSub.Websockets.Example.NetStandard/TwitchLib.EventSub.Websockets.Example.NetStandard.csproj b/TwitchLib.EventSub.Websockets.Example.NetStandard/TwitchLib.EventSub.Websockets.Example.NetStandard.csproj index c7f6f82..09e0246 100644 --- a/TwitchLib.EventSub.Websockets.Example.NetStandard/TwitchLib.EventSub.Websockets.Example.NetStandard.csproj +++ b/TwitchLib.EventSub.Websockets.Example.NetStandard/TwitchLib.EventSub.Websockets.Example.NetStandard.csproj @@ -5,10 +5,12 @@ netframework4.8 disable disable + latest + diff --git a/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedService.cs b/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedService.cs index e590f75..958eca5 100644 --- a/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedService.cs +++ b/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedService.cs @@ -5,7 +5,6 @@ using System.Threading; using System.Threading.Tasks; using TwitchLib.Api; -using TwitchLib.Api.Core.Enums; using TwitchLib.EventSub.Websockets.Core.EventArgs; using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; @@ -16,7 +15,7 @@ public class WebsocketHostedService : IHostedService private readonly IConfiguration _configuration; private readonly ILogger _logger; private readonly EventSubWebsocketClient _eventSubWebsocketClient; - private readonly TwitchApi _twitchApi = new(); + private readonly TwitchAPI _twitchApi = new(); private string _userId; public WebsocketHostedService(IConfiguration configuration ,ILogger logger, EventSubWebsocketClient eventSubWebsocketClient) diff --git a/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedServiceWithoutDI.cs b/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedServiceWithoutDI.cs index bcd5565..1b4b68d 100644 --- a/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedServiceWithoutDI.cs +++ b/TwitchLib.EventSub.Websockets.Example.NetStandard/WebsocketHostedServiceWithoutDI.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using TwitchLib.Api; using TwitchLib.EventSub.Websockets.Core.EventArgs; using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; @@ -12,7 +13,7 @@ public class WebsocketHostedServiceWithoutDI : IHostedService { private readonly ILogger _logger; private readonly EventSubWebsocketClient _eventSubWebsocketClient; - private readonly TwitchApi _twitchApi = new(); + private readonly TwitchAPI _twitchApi = new(); private string _userId; public WebsocketHostedServiceWithoutDI(ILogger logger, ILoggerFactory loggerFactory) diff --git a/TwitchLib.EventSub.Websockets.Example/TwitchLib.EventSub.Websockets.Example.csproj b/TwitchLib.EventSub.Websockets.Example/TwitchLib.EventSub.Websockets.Example.csproj index ca8f5e6..28ce0c7 100644 --- a/TwitchLib.EventSub.Websockets.Example/TwitchLib.EventSub.Websockets.Example.csproj +++ b/TwitchLib.EventSub.Websockets.Example/TwitchLib.EventSub.Websockets.Example.csproj @@ -8,6 +8,7 @@ + diff --git a/TwitchLib.EventSub.Websockets.Example/WebsocketHostedService.cs b/TwitchLib.EventSub.Websockets.Example/WebsocketHostedService.cs index af8678a..7b3e3a4 100644 --- a/TwitchLib.EventSub.Websockets.Example/WebsocketHostedService.cs +++ b/TwitchLib.EventSub.Websockets.Example/WebsocketHostedService.cs @@ -15,7 +15,7 @@ public class WebsocketHostedService : IHostedService { private readonly ILogger _logger; private readonly EventSubWebsocketClient _eventSubWebsocketClient; - private readonly TwitchApi _twitchApi = new(); + private readonly TwitchAPI _twitchApi = new(); private string _userId; public WebsocketHostedService(ILogger logger, EventSubWebsocketClient eventSubWebsocketClient) diff --git a/TwitchLib.EventSub.Websockets/Client/WebsocketClient.cs b/TwitchLib.EventSub.Websockets/Client/WebsocketClient.cs index 4b285b9..11bc032 100644 --- a/TwitchLib.EventSub.Websockets/Client/WebsocketClient.cs +++ b/TwitchLib.EventSub.Websockets/Client/WebsocketClient.cs @@ -29,17 +29,17 @@ public class WebsocketClient : IDisposable /// public bool IsFaulted => _webSocket.CloseStatus != WebSocketCloseStatus.Empty && _webSocket.CloseStatus != WebSocketCloseStatus.NormalClosure; - internal event AsyncEventHandler OnDataReceived; - internal event AsyncEventHandler OnErrorOccurred; + internal event AsyncEventHandler? OnDataReceived; + internal event AsyncEventHandler? OnErrorOccurred; - private readonly ClientWebSocket _webSocket; - private readonly ILogger _logger; + private ClientWebSocket _webSocket; + private readonly ILogger? _logger; /// /// Constructor to create a new Websocket client with a logger /// /// Logger used by the websocket client to print various state info - public WebsocketClient(ILogger logger = null) + public WebsocketClient(ILogger? logger = null) { _webSocket = new ClientWebSocket(); _logger = logger; @@ -56,6 +56,8 @@ public async Task ConnectAsync(Uri url) { if (_webSocket.State is WebSocketState.Open or WebSocketState.Connecting) return true; + if (_webSocket.State is WebSocketState.Closed) //after a socken is closed it cannot be reopened + _webSocket = new(); await _webSocket.ConnectAsync(url, CancellationToken.None); @@ -92,7 +94,6 @@ public async Task DisconnectAsync() } } -#if NET6_0_OR_GREATER /// /// Background operation to process incoming data via the websocket /// @@ -100,38 +101,43 @@ public async Task DisconnectAsync() /// private async Task ProcessDataAsync() { - const int minimumBufferSize = 256; - var storeSize = 4096; - var decoder = Encoding.UTF8.GetDecoder(); - - var store = MemoryPool.Shared.Rent(storeSize).Memory; - var buffer = MemoryPool.Shared.Rent(minimumBufferSize).Memory; - + const int bufferLength = 4096; +#if NETSTANDARD2_0 + var buffer = new ArraySegment(new byte[bufferLength]); +#else + var buffer = new Memory(new byte[bufferLength]); +#endif + var store = new byte[4096]; var payloadSize = 0; while (IsConnected) { try { +#if NETSTANDARD2_0 + WebSocketReceiveResult receiveResult; +#else ValueWebSocketReceiveResult receiveResult; +#endif do { receiveResult = await _webSocket.ReceiveAsync(buffer, CancellationToken.None); - - if (payloadSize + receiveResult.Count >= storeSize) + + if (payloadSize + receiveResult.Count >= store.Length) { - storeSize += -#if NET8_0_OR_GREATER - int.Max(4096, receiveResult.Count); -#else - Math.Max(4096, receiveResult.Count); -#endif - var newStore = MemoryPool.Shared.Rent(storeSize).Memory; - store.CopyTo(newStore); + var newStoreLength = store.Length + Math.Max(bufferLength, receiveResult.Count); + var newStore = new byte[newStoreLength]; + store.AsSpan().CopyTo(newStore); store = newStore; } - buffer.CopyTo(store[payloadSize..]); + buffer +#if NETSTANDARD2_0 + .Array.AsSpan(0, receiveResult.Count) +#else + .Span.Slice(0, receiveResult.Count) +#endif + .CopyTo(store.AsSpan(payloadSize)); payloadSize += receiveResult.Count; } while (!receiveResult.EndOfMessage); @@ -139,19 +145,12 @@ private async Task ProcessDataAsync() switch (receiveResult.MessageType) { case WebSocketMessageType.Text: - { - var intermediate = MemoryPool.Shared.Rent(payloadSize).Memory; - if (payloadSize == 0) continue; - decoder.Convert(store.Span[..payloadSize], intermediate.Span, true, out _, out var charsCount, out _); - var message = intermediate[..charsCount]; - - OnDataReceived?.Invoke(this, new DataReceivedArgs { Message = message.Span.ToString() }); + OnDataReceived?.Invoke(this, new DataReceivedArgs { Bytes = store.AsSpan(0, payloadSize).ToArray() }); payloadSize = 0; break; - } case WebSocketMessageType.Binary: break; case WebSocketMessageType.Close: @@ -168,75 +167,6 @@ private async Task ProcessDataAsync() } } } -#else - /// - /// Background operation to process incoming data via the websocket - /// - /// Task representing the background operation - /// - private async Task ProcessDataAsync() - { - const int minimumBufferSize = 8192; - - var buffer = new ArraySegment(new byte[minimumBufferSize]); - var payloadSize = 0; - - while (IsConnected) - { - try - { - WebSocketReceiveResult receiveResult; - var memory = new MemoryStream(); - - do - { - receiveResult = await _webSocket.ReceiveAsync(buffer, CancellationToken.None); - - if (buffer.Array == null) - continue; - -#pragma warning disable CA1849 - memory.Write(buffer.Array, buffer.Offset, receiveResult.Count); -#pragma warning restore CA1849 - payloadSize += receiveResult.Count; - } while (!receiveResult.EndOfMessage); - - switch (receiveResult.MessageType) - { - case WebSocketMessageType.Text: - { - if (payloadSize == 0) - continue; - - memory.Seek(0, SeekOrigin.Begin); - - var reader = new StreamReader(memory, Encoding.UTF8); - - OnDataReceived?.Invoke(this, new DataReceivedArgs { Message = await reader.ReadToEndAsync() }); - - memory.Dispose(); - reader.Dispose(); - break; - } - case WebSocketMessageType.Binary: - break; - case WebSocketMessageType.Close: - if (_webSocket.CloseStatus != null) - _logger?.LogWebsocketClosed((WebSocketCloseStatus)_webSocket.CloseStatus!, _webSocket.CloseStatusDescription!); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - - catch (Exception ex) - { - OnErrorOccurred?.Invoke(this, new ErrorOccuredArgs { Exception = ex }); - break; - } - } - } -#endif /// /// Cleanup of any unused resources as per IDisposable guidelines diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatClearArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatClearArgs.cs new file mode 100644 index 0000000..a1b36ba --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatClearArgs.cs @@ -0,0 +1,7 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; + +public class ChannelChatClearArgs : TwitchLibEventSubEventArgs> +{ } diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatClearUserMessagesArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatClearUserMessagesArgs.cs new file mode 100644 index 0000000..e4ff9ef --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatClearUserMessagesArgs.cs @@ -0,0 +1,7 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; + +public class ChannelChatClearUserMessagesArgs : TwitchLibEventSubEventArgs> +{ } diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatMessageDeleteArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatMessageDeleteArgs.cs new file mode 100644 index 0000000..bf7c5c5 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatMessageDeleteArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelChatMessageDeleteArgs : TwitchLibEventSubEventArgs> + { + } +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatNotificationArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatNotificationArgs.cs new file mode 100644 index 0000000..43bb582 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelChatNotificationArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelChatNotificationArgs : TwitchLibEventSubEventArgs> + { + } +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelPointsAutomaticRewardRedemptionArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelPointsAutomaticRewardRedemptionArgs.cs new file mode 100644 index 0000000..d57b9f7 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelPointsAutomaticRewardRedemptionArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; + +public class ChannelPointsAutomaticRewardRedemptionArgs : TwitchLibEventSubEventArgs> +{ +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionBeginArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionBeginArgs.cs new file mode 100644 index 0000000..76a0403 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionBeginArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelSharedChatSessionBeginArgs : TwitchLibEventSubEventArgs> + { } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionEndArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionEndArgs.cs new file mode 100644 index 0000000..82c81fb --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionEndArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelSharedChatSessionEndArgs : TwitchLibEventSubEventArgs> + { } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionUpdateArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionUpdateArgs.cs new file mode 100644 index 0000000..d996faa --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSharedChatSessionUpdateArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelSharedChatSessionUpdateArgs : TwitchLibEventSubEventArgs> + { } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSuspiciousUserMessageArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSuspiciousUserMessageArgs.cs new file mode 100644 index 0000000..e0db5b7 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSuspiciousUserMessageArgs.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelSuspiciousUserMessageArgs : TwitchLibEventSubEventArgs> + { + } +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSuspiciousUserUpdateArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSuspiciousUserUpdateArgs.cs new file mode 100644 index 0000000..5b8d851 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelSuspiciousUserUpdateArgs.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public sealed class ChannelSuspiciousUserUpdateArgs : TwitchLibEventSubEventArgs> + { + } +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelUnbanRequestCreateArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelUnbanRequestCreateArgs.cs new file mode 100644 index 0000000..50226f1 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelUnbanRequestCreateArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelUnbanRequestCreateArgs : TwitchLibEventSubEventArgs> + { } +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelUnbanRequestResolveArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelUnbanRequestResolveArgs.cs new file mode 100644 index 0000000..a86c678 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelUnbanRequestResolveArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelUnbanRequestResolveArgs : TwitchLibEventSubEventArgs> + { } +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelVipArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelVipArgs.cs new file mode 100644 index 0000000..4e7d517 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelVipArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelVipArgs : TwitchLibEventSubEventArgs> + { } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelWarningAcknowledgeArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelWarningAcknowledgeArgs.cs new file mode 100644 index 0000000..acfad2e --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelWarningAcknowledgeArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelWarningAcknowledgeArgs : TwitchLibEventSubEventArgs> + { } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelWarningSendArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelWarningSendArgs.cs new file mode 100644 index 0000000..e05d67a --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/Channel/ChannelWarningSendArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.Channel +{ + public class ChannelWarningSendArgs : TwitchLibEventSubEventArgs> + { } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/DataReceivedArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/DataReceivedArgs.cs index 292d6cf..4c2fdbf 100644 --- a/TwitchLib.EventSub.Websockets/Core/EventArgs/DataReceivedArgs.cs +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/DataReceivedArgs.cs @@ -1,7 +1,6 @@ -namespace TwitchLib.EventSub.Websockets.Core.EventArgs +namespace TwitchLib.EventSub.Websockets.Core.EventArgs; + +internal class DataReceivedArgs : System.EventArgs { - internal class DataReceivedArgs : System.EventArgs - { - public string Message { get; internal set; } - } -} \ No newline at end of file + public byte[] Bytes { get; internal set; } +} diff --git a/TwitchLib.EventSub.Websockets/Core/EventArgs/User/UserWhisperMessageArgs.cs b/TwitchLib.EventSub.Websockets/Core/EventArgs/User/UserWhisperMessageArgs.cs new file mode 100644 index 0000000..297cdd5 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Core/EventArgs/User/UserWhisperMessageArgs.cs @@ -0,0 +1,8 @@ +using TwitchLib.EventSub.Core.SubscriptionTypes.User; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Core.EventArgs.User +{ + public class UserWhisperMessageArgs : TwitchLibEventSubEventArgs> + { } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/Models/EventSubMetadata.cs b/TwitchLib.EventSub.Websockets/Core/Models/EventSubMetadata.cs index 8cb527e..1c0c63b 100644 --- a/TwitchLib.EventSub.Websockets/Core/Models/EventSubMetadata.cs +++ b/TwitchLib.EventSub.Websockets/Core/Models/EventSubMetadata.cs @@ -1,13 +1,31 @@ using System; -namespace TwitchLib.EventSub.Websockets.Core.Models +namespace TwitchLib.EventSub.Websockets.Core.Models; + +public class EventSubMetadata { - public class EventSubMetadata - { - public string MessageId { get; set; } - public string MessageType { get; set; } - public DateTime MessageTimestamp { get; set; } - public string SubscriptionType { get; set; } - public string SubscriptionVersion { get; set; } - } + /// + /// An ID that uniquely identifies message. + /// + public string MessageId { get; set; } + + /// + /// The type of notification. + /// + public string MessageType { get; set; } + + /// + /// The UTC date and time that Twitch sent the notification. + /// + public DateTime MessageTimestamp { get; set; } + + /// + /// The subscription type. + /// + public string? SubscriptionType { get; set; } + + /// + /// The subscription version. + /// + public string? SubscriptionVersion { get; set; } } \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/Models/EventSubNotificationPayload.cs b/TwitchLib.EventSub.Websockets/Core/Models/EventSubNotificationPayload.cs index 2b1997a..fad563e 100644 --- a/TwitchLib.EventSub.Websockets/Core/Models/EventSubNotificationPayload.cs +++ b/TwitchLib.EventSub.Websockets/Core/Models/EventSubNotificationPayload.cs @@ -3,6 +3,10 @@ public class EventSubNotificationPayload { public EventSubTransport Transport { get; set; } + + /// + /// The event’s data. + /// public T Event { get; set; } } } \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Core/Models/EventSubWebsocketSessionInfo.cs b/TwitchLib.EventSub.Websockets/Core/Models/EventSubWebsocketSessionInfo.cs index 6994b91..903e831 100644 --- a/TwitchLib.EventSub.Websockets/Core/Models/EventSubWebsocketSessionInfo.cs +++ b/TwitchLib.EventSub.Websockets/Core/Models/EventSubWebsocketSessionInfo.cs @@ -1,16 +1,34 @@ using System; -namespace TwitchLib.EventSub.Websockets.Core.Models +namespace TwitchLib.EventSub.Websockets.Core.Models; + +public class EventSubWebsocketSessionInfo { - public class EventSubWebsocketSessionInfo - { - public string Id { get; set; } - public string Status { get; set; } - public string DisconnectReason { get; set; } - public int? KeepaliveTimeoutSeconds { get; set; } - public string ReconnectUrl { get; set; } - public DateTime ConnectedAt { get; set; } - public DateTime? DisconnectedAt { get; set; } - public DateTime? ReconnectingAt { get; set; } - } + /// + /// An ID that uniquely identifies this WebSocket connection. + /// + public string Id { get; set; } + + /// + /// The connection’s status. + /// + public string Status { get; set; } + public string DisconnectReason { get; set; } + + /// + /// The maximum number of seconds that you should expect silence before receiving a keepalive message. + /// + public int? KeepaliveTimeoutSeconds { get; set; } + + /// + /// The URL to reconnect to. + /// + public string? ReconnectUrl { get; set; } + + /// + /// The UTC date and time when the connection was created. + /// + public DateTime ConnectedAt { get; set; } + public DateTime? DisconnectedAt { get; set; } + public DateTime? ReconnectingAt { get; set; } } \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/EventSubWebsocketClient.cs b/TwitchLib.EventSub.Websockets/EventSubWebsocketClient.cs index 75cd279..7c54122 100644 --- a/TwitchLib.EventSub.Websockets/EventSubWebsocketClient.cs +++ b/TwitchLib.EventSub.Websockets/EventSubWebsocketClient.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -32,239 +33,320 @@ public class EventSubWebsocketClient /// /// Event that triggers when the websocket was successfully connected /// - public event AsyncEventHandler WebsocketConnected; + public event AsyncEventHandler? WebsocketConnected; /// /// Event that triggers when the websocket disconnected /// - public event AsyncEventHandler WebsocketDisconnected; + public event AsyncEventHandler? WebsocketDisconnected; /// /// Event that triggers when an error occurred on the websocket /// - public event AsyncEventHandler ErrorOccurred; + public event AsyncEventHandler? ErrorOccurred; /// /// Event that triggers when the websocket was successfully reconnected /// - public event AsyncEventHandler WebsocketReconnected; + public event AsyncEventHandler? WebsocketReconnected; /// /// Event that triggers on "channel.ad_break.begin" notifications /// - public event AsyncEventHandler ChannelAdBreakBegin; + public event AsyncEventHandler? ChannelAdBreakBegin; /// /// Event that triggers on "channel.ban" notifications /// - public event AsyncEventHandler ChannelBan; + public event AsyncEventHandler? ChannelBan; /// /// Event that triggers on "channel.charity_campaign.start" notifications /// - public event AsyncEventHandler ChannelCharityCampaignStart; + public event AsyncEventHandler? ChannelCharityCampaignStart; /// /// Event that triggers on "channel.charity_campaign.donate" notifications /// - public event AsyncEventHandler ChannelCharityCampaignDonate; + public event AsyncEventHandler? ChannelCharityCampaignDonate; /// /// Event that triggers on "channel.charity_campaign.progress" notifications /// - public event AsyncEventHandler ChannelCharityCampaignProgress; + public event AsyncEventHandler? ChannelCharityCampaignProgress; /// /// Event that triggers on "channel.charity_campaign.stop" notifications /// - public event AsyncEventHandler ChannelCharityCampaignStop; + public event AsyncEventHandler? ChannelCharityCampaignStop; + + /// + /// Event that triggers on channel.chat.clear notifications + /// + public event AsyncEventHandler? ChannelChatClear; + /// + /// Event that triggers on channel.chat.clear_user_messages notifications + /// + public event AsyncEventHandler? ChannelChatClearUserMessages; /// /// Event that triggers on channel.chat.message notifications /// - public event AsyncEventHandler ChannelChatMessage; + public event AsyncEventHandler? ChannelChatMessage; + /// + /// Event that triggers on "channel.chat.message_delete" notifications + /// + public event AsyncEventHandler? ChannelChatMessageDelete; + /// + /// Event that triggers on "channel.chat.notification" notifications + /// + public event AsyncEventHandler? ChannelChatNotification; /// /// Event that triggers on "channel.cheer" notifications /// - public event AsyncEventHandler ChannelCheer; + public event AsyncEventHandler? ChannelCheer; /// /// Event that triggers on "channel.follow" notifications /// - public event AsyncEventHandler ChannelFollow; + public event AsyncEventHandler? ChannelFollow; /// /// Event that triggers on "channel.goal.begin" notifications /// - public event AsyncEventHandler ChannelGoalBegin; + public event AsyncEventHandler? ChannelGoalBegin; /// /// Event that triggers on "channel.goal.end" notifications /// - public event AsyncEventHandler ChannelGoalEnd; + public event AsyncEventHandler? ChannelGoalEnd; /// /// Event that triggers on "channel.goal.progress" notifications /// - public event AsyncEventHandler ChannelGoalProgress; + public event AsyncEventHandler? ChannelGoalProgress; /// /// Event that triggers on "channel.guest_star_guest.update" notifications /// - public event AsyncEventHandler ChannelGuestStarGuestUpdate; + public event AsyncEventHandler? ChannelGuestStarGuestUpdate; /// /// Event that triggers on "channel.guest_star_session.begin" notifications /// - public event AsyncEventHandler ChannelGuestStarSessionBegin; + public event AsyncEventHandler? ChannelGuestStarSessionBegin; /// /// Event that triggers on "channel.guest_star_guest.update" notifications /// - public event AsyncEventHandler ChannelGuestStarSessionEnd; + public event AsyncEventHandler? ChannelGuestStarSessionEnd; /// /// Event that triggers on "channel.guest_star_settings.update" notifications /// - public event AsyncEventHandler ChannelGuestStarSettingsUpdate; + public event AsyncEventHandler? ChannelGuestStarSettingsUpdate; /// /// Event that triggers on "channel.guest_star_slot.update" notifications /// - public event AsyncEventHandler ChannelGuestStarSlotUpdate; + public event AsyncEventHandler? ChannelGuestStarSlotUpdate; /// /// Event that triggers on "channel.hype_train.begin" notifications /// - public event AsyncEventHandler ChannelHypeTrainBegin; + public event AsyncEventHandler? ChannelHypeTrainBegin; /// /// Event that triggers on "channel.hype_train.end" notifications /// - public event AsyncEventHandler ChannelHypeTrainEnd; + public event AsyncEventHandler? ChannelHypeTrainEnd; /// /// Event that triggers on "channel.hype_train.progress" notifications /// - public event AsyncEventHandler ChannelHypeTrainProgress; + public event AsyncEventHandler? ChannelHypeTrainProgress; /// /// Event that triggers on "channel.moderator.add" notifications /// - public event AsyncEventHandler ChannelModeratorAdd; + public event AsyncEventHandler? ChannelModeratorAdd; /// /// Event that triggers on "channel.moderator.remove" notifications /// - public event AsyncEventHandler ChannelModeratorRemove; + public event AsyncEventHandler? ChannelModeratorRemove; + + /// + /// Event that triggers on "channel.vip.add" notifications + /// + public event AsyncEventHandler? ChannelVipAdd; + /// + /// Event that triggers on "channel.vip.remove" notifications + /// + public event AsyncEventHandler? ChannelVipRemove; /// /// Event that triggers on "channel.channel_points_custom_reward.add" notifications /// - public event AsyncEventHandler ChannelPointsCustomRewardAdd; + public event AsyncEventHandler? ChannelPointsCustomRewardAdd; /// /// Event that triggers on "channel.channel_points_custom_reward.remove" notifications /// - public event AsyncEventHandler ChannelPointsCustomRewardRemove; + public event AsyncEventHandler? ChannelPointsCustomRewardRemove; /// /// Event that triggers on "channel.channel_points_custom_reward.update" notifications /// - public event AsyncEventHandler ChannelPointsCustomRewardUpdate; + public event AsyncEventHandler? ChannelPointsCustomRewardUpdate; + + /// + /// Event that triggers on "channel.channel_points_automatic_reward_redemption.add" notifications + /// + public event AsyncEventHandler? ChannelPointsAutomaticRewardRedemptionAdd; /// /// Event that triggers on "channel.channel_points_custom_reward_redemption.add" notifications /// - public event AsyncEventHandler ChannelPointsCustomRewardRedemptionAdd; + public event AsyncEventHandler? ChannelPointsCustomRewardRedemptionAdd; /// /// Event that triggers on "channel.channel_points_custom_reward_redemption.update" notifications /// - public event AsyncEventHandler ChannelPointsCustomRewardRedemptionUpdate; + public event AsyncEventHandler? ChannelPointsCustomRewardRedemptionUpdate; /// /// Event that triggers on "channel.poll.begin" notifications /// - public event AsyncEventHandler ChannelPollBegin; + public event AsyncEventHandler? ChannelPollBegin; /// /// Event that triggers on "channel.poll.end" notifications /// - public event AsyncEventHandler ChannelPollEnd; + public event AsyncEventHandler? ChannelPollEnd; /// /// Event that triggers on "channel.poll.progress" notifications /// - public event AsyncEventHandler ChannelPollProgress; + public event AsyncEventHandler? ChannelPollProgress; /// /// Event that triggers on "channel.prediction.begin" notifications /// - public event AsyncEventHandler ChannelPredictionBegin; + public event AsyncEventHandler? ChannelPredictionBegin; /// /// Event that triggers on "channel.prediction.end" notifications /// - public event AsyncEventHandler ChannelPredictionEnd; + public event AsyncEventHandler? ChannelPredictionEnd; /// /// Event that triggers on "channel.prediction.lock" notifications /// - public event AsyncEventHandler ChannelPredictionLock; + public event AsyncEventHandler? ChannelPredictionLock; /// /// Event that triggers on "channel.prediction.progress" notifications /// - public event AsyncEventHandler ChannelPredictionProgress; + public event AsyncEventHandler? ChannelPredictionProgress; /// /// Event that triggers on "channel.raid" notifications /// - public event AsyncEventHandler ChannelRaid; + public event AsyncEventHandler? ChannelRaid; /// /// Event that triggers on "channel.shield_mode.begin" notifications /// - public event AsyncEventHandler ChannelShieldModeBegin; + public event AsyncEventHandler? ChannelShieldModeBegin; /// /// Event that triggers on "channel.shield_mode.end" notifications /// - public event AsyncEventHandler ChannelShieldModeEnd; + public event AsyncEventHandler? ChannelShieldModeEnd; /// /// Event that triggers on "channel.shoutout.create" notifications /// - public event AsyncEventHandler ChannelShoutoutCreate; + public event AsyncEventHandler? ChannelShoutoutCreate; /// /// Event that triggers on "channel.shoutout.receive" notifications /// - public event AsyncEventHandler ChannelShoutoutReceive; + public event AsyncEventHandler? ChannelShoutoutReceive; /// /// Event that triggers on "channel.subscribe" notifications /// - public event AsyncEventHandler ChannelSubscribe; + public event AsyncEventHandler? ChannelSubscribe; /// /// Event that triggers on "channel.subscription.end" notifications /// - public event AsyncEventHandler ChannelSubscriptionEnd; + public event AsyncEventHandler? ChannelSubscriptionEnd; /// /// Event that triggers on "channel.subscription.gift" notifications /// - public event AsyncEventHandler ChannelSubscriptionGift; + public event AsyncEventHandler? ChannelSubscriptionGift; /// /// Event that triggers on "channel.subscription.message" notifications /// - public event AsyncEventHandler ChannelSubscriptionMessage; + public event AsyncEventHandler? ChannelSubscriptionMessage; + + /// + /// Event that triggers on "channel.suspicious_user.message" notifications + /// + public event AsyncEventHandler? ChannelSuspiciousUserMessage; + + /// + /// Event that triggers on "channel.suspicious_user.update" notifications + /// + public event AsyncEventHandler? ChannelSuspiciousUserUpdate; + + /// + /// Event that triggers on "channel.warning.acknowledge" notifications + /// + public event AsyncEventHandler? ChannelWarningAcknowledge; + + /// + /// Event that triggers on "channel.warning.send" notifications + /// + public event AsyncEventHandler? ChannelWarningSend; /// /// Event that triggers on "channel.unban" notifications /// - public event AsyncEventHandler ChannelUnban; + public event AsyncEventHandler? ChannelUnban; /// /// Event that triggers on "channel.update" notifications /// - public event AsyncEventHandler ChannelUpdate; + public event AsyncEventHandler? ChannelUpdate; /// /// Event that triggers on "stream.offline" notifications /// - public event AsyncEventHandler StreamOffline; + public event AsyncEventHandler? StreamOffline; /// /// Event that triggers on "stream.online" notifications /// - public event AsyncEventHandler StreamOnline; + public event AsyncEventHandler? StreamOnline; /// /// Event that triggers on "user.update" notifications /// - public event AsyncEventHandler UserUpdate; + public event AsyncEventHandler? UserUpdate; + + /// + /// Event that triggers on "user.whisper.message" notifications + /// + public event AsyncEventHandler? UserWhisperMessage; + + /// + /// Event that triggers on "channel.shared_chat.begin" notifications + /// + public event AsyncEventHandler? ChannelSharedChatSessionBegin; + + /// + /// Event that triggers on "channel.shared_chat.update" notifications + /// + public event AsyncEventHandler? ChannelSharedChatSessionUpdate; + + /// + /// Event that triggers on "channel.shared_chat.end" notifications + /// + public event AsyncEventHandler? ChannelSharedChatSessionEnd; + + /// + /// Event that triggers on "channel.unban_request.create" notifications + /// + public event AsyncEventHandler? ChannelUnbanRequestCreate; + + /// + /// Event that triggers on "channel.unban_request.resolve" notifications + /// + public event AsyncEventHandler? ChannelUnbanRequestResolve; #endregion /// /// Id associated with the Websocket Session. Needed for creating subscriptions for the socket. /// - public string SessionId { get; private set; } + public string SessionId { get; private set; } = string.Empty; - private CancellationTokenSource _cts; + private CancellationTokenSource? _cts; private DateTimeOffset _lastReceived = DateTimeOffset.MinValue; private TimeSpan _keepAliveTimeout = TimeSpan.Zero; @@ -273,11 +355,11 @@ public class EventSubWebsocketClient private bool _reconnectComplete; private WebsocketClient _websocketClient; - private Dictionary> _handlers; + private readonly Dictionary> _handlers = new(); private readonly ILogger _logger; - private readonly ILoggerFactory _loggerFactory; - private readonly IServiceProvider _serviceProvider; + private readonly ILoggerFactory? _loggerFactory; + private readonly IServiceProvider? _serviceProvider; private readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions { @@ -315,7 +397,7 @@ public EventSubWebsocketClient(ILogger logger, IEnumera /// Instantiates an EventSubWebsocketClient used to subscribe to EventSub notifications via Websockets. /// /// LoggerFactory used to construct Loggers for the EventSubWebsocketClient and underlying classes - public EventSubWebsocketClient(ILoggerFactory loggerFactory = null) + public EventSubWebsocketClient(ILoggerFactory? loggerFactory = null) { _loggerFactory = loggerFactory; @@ -347,7 +429,7 @@ public EventSubWebsocketClient(ILoggerFactory loggerFactory = null) /// /// Optional url param to be able to connect to reconnect urls provided by Twitch or test servers /// true: Connection successful false: Connection failed - public async Task ConnectAsync(Uri url = null) + public async Task ConnectAsync(Uri? url = null) { url = url ?? new Uri(WEBSOCKET_URL); _lastReceived = DateTimeOffset.MinValue; @@ -464,8 +546,6 @@ private async Task ReconnectAsync(Uri url) /// Enumerable of handlers that are responsible for acting on a specified subscription type private void PrepareHandlers(IEnumerable handlers) { - _handlers = _handlers ?? new Dictionary>(); - foreach (var handler in handlers) { #if NET6_0_OR_GREATER @@ -506,24 +586,25 @@ private async Task ConnectionCheckAsync() /// EventArgs send with the event. private async Task OnDataReceived(object sender, DataReceivedArgs e) { + _logger?.LogMessage(e.Bytes); _lastReceived = DateTimeOffset.Now; - var json = JsonDocument.Parse(e.Message); + var json = JsonDocument.Parse(e.Bytes); var metadata = json.RootElement.GetProperty("metadata"u8); var messageType = metadata.GetProperty("message_type"u8).GetString(); switch (messageType) { case "session_welcome": - await HandleWelcome(e.Message); + await HandleWelcome(e.Bytes); break; case "session_disconnect": - await HandleDisconnect(e.Message); + await HandleDisconnect(e.Bytes); break; case "session_reconnect": - HandleReconnect(e.Message); + HandleReconnect(e.Bytes); break; case "session_keepalive": - HandleKeepAlive(e.Message); + HandleKeepAlive(e.Bytes); break; case "notification": var subscriptionType = metadata.GetProperty("subscription_type"u8).GetString(); @@ -532,14 +613,15 @@ private async Task OnDataReceived(object sender, DataReceivedArgs e) await ErrorOccurred.InvokeAsync(this, new ErrorOccuredArgs { Exception = new ArgumentNullException(nameof(subscriptionType)), Message = "Unable to determine subscription type!" }); break; } - HandleNotification(e.Message, subscriptionType); + var message1 = Encoding.UTF8.GetString(e.Bytes); + HandleNotification(message1, subscriptionType!); break; case "revocation": - HandleRevocation(e.Message); + var message2 = Encoding.UTF8.GetString(e.Bytes); + HandleRevocation(message2); break; default: _logger?.LogUnknownMessageType(messageType); - _logger?.LogMessage(e.Message); break; } } @@ -558,22 +640,20 @@ private async Task OnErrorOccurred(object sender, ErrorOccuredArgs e) /// Handles 'session_reconnect' notifications /// /// notification message received from Twitch EventSub - private void HandleReconnect(string message) + private void HandleReconnect(byte[] message) { _logger?.LogReconnectRequested(SessionId); var data = JsonSerializer.Deserialize(message, _jsonSerializerOptions); _reconnectRequested = true; Task.Run(async () => await ReconnectAsync(new Uri(data?.Payload.Session.ReconnectUrl ?? WEBSOCKET_URL))); - - _logger?.LogMessage(message); } /// /// Handles 'session_welcome' notifications /// /// notification message received from Twitch EventSub - private async ValueTask HandleWelcome(string message) + private async ValueTask HandleWelcome(byte[] message) { var data = JsonSerializer.Deserialize(message, _jsonSerializerOptions); @@ -589,21 +669,19 @@ private async ValueTask HandleWelcome(string message) _keepAliveTimeout = TimeSpan.FromSeconds(keepAliveTimeout ?? 10); await WebsocketConnected.InvokeAsync(this, new WebsocketConnectedArgs { IsRequestedReconnect = _reconnectRequested }); - - _logger?.LogMessage(message); } /// /// Handles 'session_disconnect' notifications /// /// notification message received from Twitch EventSub - private async Task HandleDisconnect(string message) + private async Task HandleDisconnect(byte[] message) { var data = JsonSerializer.Deserialize(message); if (data != null) _logger?.LogForceDisconnected(data.Payload.Session.Id, data.Payload.Session.DisconnectedAt, data.Payload.Session.DisconnectReason); - + await WebsocketDisconnected.InvokeAsync(this, EventArgs.Empty); } @@ -611,9 +689,9 @@ private async Task HandleDisconnect(string message) /// Handles 'session_keepalive' notifications /// /// notification message received from Twitch EventSub - private void HandleKeepAlive(string message) + private void HandleKeepAlive(byte[] message) { - _logger?.LogMessage(message); + _ = message; } /// @@ -623,10 +701,8 @@ private void HandleKeepAlive(string message) /// subscription type received from Twitch EventSub private void HandleNotification(string message, string subscriptionType) { - if (_handlers != null && _handlers.TryGetValue(subscriptionType, out var handler)) + if (_handlers.TryGetValue(subscriptionType, out var handler)) handler(this, message, _jsonSerializerOptions); - - _logger?.LogMessage(message); } /// @@ -635,10 +711,8 @@ private void HandleNotification(string message, string subscriptionType) /// notification message received from Twitch EventSub private void HandleRevocation(string message) { - if (_handlers != null && _handlers.TryGetValue("revocation", out var handler)) + if (_handlers.TryGetValue("revocation", out var handler)) handler(this, message, _jsonSerializerOptions); - - _logger?.LogMessage(message); } /// @@ -646,16 +720,26 @@ private void HandleRevocation(string message) /// /// name of the event to raise /// args to pass with the event - internal void RaiseEvent(string eventName, object args = null) + internal async void RaiseEvent(string eventName, object? args = null) { var fInfo = GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic); if (fInfo?.GetValue(this) is not MulticastDelegate multi) return; + var parameters = new object[] { this, args ?? EventArgs.Empty }; foreach (var del in multi.GetInvocationList()) { - del.Method.Invoke(del.Target, args == null ? new object[] { this, EventArgs.Empty } : new[] { this, args }); + try + { + var result = del.Method.Invoke(del.Target, parameters); + if (result is Task task) + await task; + } + catch (Exception ex) + { + _logger.LogRaiseEventExeption(eventName, ex); + } } } } diff --git a/TwitchLib.EventSub.Websockets/Extensions/LogExtensions.cs b/TwitchLib.EventSub.Websockets/Extensions/LogExtensions.cs index 31656e3..9203519 100644 --- a/TwitchLib.EventSub.Websockets/Extensions/LogExtensions.cs +++ b/TwitchLib.EventSub.Websockets/Extensions/LogExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Net.WebSockets; +using System.Text; using Microsoft.Extensions.Logging; using TwitchLib.EventSub.Websockets.Client; @@ -7,9 +8,14 @@ namespace TwitchLib.EventSub.Websockets.Extensions { internal static partial class LogExtensions { - [LoggerMessage(LogLevel.Debug, "{message}")] + const LogLevel LogMessageLogLevel = LogLevel.Debug; + + [LoggerMessage(LogLevel.Error, "Exeption was throw when raising '{eventName}' event.")] + public static partial void LogRaiseEventExeption(this ILogger logger, string eventName, Exception ex); + + [LoggerMessage(LogMessageLogLevel, "{message}")] public static partial void LogMessage(this ILogger logger, string message); - + [LoggerMessage(LogLevel.Critical, "Websocket {sessionId} disconnected at {disconnectedAt}. Reason: {disconnectReason}")] public static partial void LogForceDisconnected(this ILogger logger, string sessionId, DateTime? disconnectedAt, string disconnectReason); @@ -24,5 +30,13 @@ internal static partial class LogExtensions [LoggerMessage(LogLevel.Critical, "{closeStatus} - {closeStatusDescription}")] public static partial void LogWebsocketClosed(this ILogger logger, WebSocketCloseStatus closeStatus, string closeStatusDescription); + + public static void LogMessage(this ILogger logger, byte[] message) + { + if (logger.IsEnabled(LogMessageLogLevel)) + { + __LogMessageCallback(logger, Encoding.UTF8.GetString(message), null); + } + } } -} \ No newline at end of file +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/ChannelPoints/Redemptions/ChannelPointsAutomaticRewardRedemptionAddHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/ChannelPoints/Redemptions/ChannelPointsAutomaticRewardRedemptionAddHandler.cs new file mode 100644 index 0000000..6ae04f6 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/ChannelPoints/Redemptions/ChannelPointsAutomaticRewardRedemptionAddHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.ChannelPoints.Redemptions; +/// +/// Handler for 'channel.channel_points_automatic_reward_redemption.add' notifications +/// +public class ChannelPointsAutomaticRewardRedemptionAddHandler : INotificationHandler +{ + /// + public string SubscriptionType => "channel.channel_points_automatic_reward_redemption.add"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelPointsAutomaticRewardRedemptionAdd", new ChannelPointsAutomaticRewardRedemptionArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChannelChatClearHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChannelChatClearHandler.cs new file mode 100644 index 0000000..5fd0d20 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChannelChatClearHandler.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Chat; + +/// +/// Handler for 'channel.chat.clear' notifications +/// +public class ChannelChatClearHandler : INotificationHandler +{ + /// + public string SubscriptionType => "channel.chat.clear"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelChatClear", new ChannelChatClearArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChannelChatClearUserMessagesHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChannelChatClearUserMessagesHandler.cs new file mode 100644 index 0000000..2931c65 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChannelChatClearUserMessagesHandler.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Chat; + +/// +/// Handler for 'channel.chat.clear_user_messages' notifications +/// +public class ChannelChatClearUserMessagesHandler : INotificationHandler +{ + /// + public string SubscriptionType => "channel.chat.clear_user_messages"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelChatClearUserMessages", new ChannelChatClearUserMessagesArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChatMessageDeleteHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChatMessageDeleteHandler.cs new file mode 100644 index 0000000..980cba1 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChatMessageDeleteHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Chat +{ + /// + /// Handler for 'channel.chat.message_delete' notifications + /// + public class ChatMessageDeleteHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.chat.message_delete"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + client.RaiseEvent("ChannelChatMessageDelete", new ChannelChatMessageDeleteArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChatNotificationHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChatNotificationHandler.cs new file mode 100644 index 0000000..9e401ed --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Chat/ChatNotificationHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Chat +{ + /// + /// Handler for 'channel.chat.notification' notifications + /// + public class ChatNotificationHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.chat.notification"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + client.RaiseEvent("ChannelChatNotification", new ChannelChatNotificationArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/ChatMessageHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/ChatMessageHandler.cs index 226fe03..07f5a6c 100644 --- a/TwitchLib.EventSub.Websockets/Handler/Channel/ChatMessageHandler.cs +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/ChatMessageHandler.cs @@ -8,10 +8,15 @@ namespace TwitchLib.EventSub.Websockets.Handler.Channel { + /// + /// Handler for 'channel.chat.nessage' notifications + /// public class ChatMessageHandler : INotificationHandler { + /// public string SubscriptionType => "channel.chat.message"; + /// public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) { try diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Moderation/ChannelUnbanRequestCreateHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Moderation/ChannelUnbanRequestCreateHandler.cs new file mode 100644 index 0000000..2e0e8f7 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Moderation/ChannelUnbanRequestCreateHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Moderation +{ + /// + /// Handler for 'channel.unban_request.create' notifications + /// + public class ChannelUnbanRequestCreateHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.unban_request.create"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelUnbanRequestCreate", new ChannelUnbanRequestCreateArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Moderation/ChannelUnbanRequestResolveHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Moderation/ChannelUnbanRequestResolveHandler.cs new file mode 100644 index 0000000..411da7e --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Moderation/ChannelUnbanRequestResolveHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Moderation +{ + /// + /// Handler for 'channel.unban_request.resolve' notifications + /// + public class ChannelUnbanRequestResolveHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.unban_request.resolve"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelUnbanRequestResolve", new ChannelUnbanRequestResolveArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionBeginHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionBeginHandler.cs new file mode 100644 index 0000000..ea2f3e1 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionBeginHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.SharedChat +{ + /// + /// Handler for 'channel.shared_chat.begin' notifications + /// + public class ChannelSharedChatSessionBeginHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.shared_chat.begin"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelSharedChatSessionBegin", new ChannelSharedChatSessionBeginArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle channel.shared_chat.begin notification! Raw Json: {jsonString}" }); + } + } + } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionEndHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionEndHandler.cs new file mode 100644 index 0000000..ef0e972 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionEndHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.SharedChat +{ + /// + /// Handler for 'channel.shared_chat.end' notifications + /// + public class ChannelSharedChatSessionEndHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.shared_chat.end"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelSharedChatSessionEnd", new ChannelSharedChatSessionEndArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle channel.shared_chat.end notification! Raw Json: {jsonString}" }); + } + } + } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionUpdateHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionUpdateHandler.cs new file mode 100644 index 0000000..0047e0b --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/SharedChat/ChannelSharedChatSessionUpdateHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.SharedChat +{ + /// + /// Handler for 'channel.shared_chat.update' notifications + /// + public class ChannelSharedChatSessionUpdateHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.shared_chat.update"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelSharedChatSessionUpdate", new ChannelSharedChatSessionUpdateArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle channel.shared_chat.update notification! Raw Json: {jsonString}" }); + } + } + } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/SuspiciousUser/ChannelSuspiciousUserMessageHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/SuspiciousUser/ChannelSuspiciousUserMessageHandler.cs new file mode 100644 index 0000000..8616f65 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/SuspiciousUser/ChannelSuspiciousUserMessageHandler.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.SuspiciousUser +{ + /// + /// Handler for 'channel.suspicious_user.message' notifications + /// + public class ChannelSuspiciousUserMessageHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.suspicious_user.message"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelSuspiciousUserMessage", new ChannelSuspiciousUserMessageArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/SuspiciousUser/ChannelSuspiciousUserUpdateHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/SuspiciousUser/ChannelSuspiciousUserUpdateHandler.cs new file mode 100644 index 0000000..af11ae8 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/SuspiciousUser/ChannelSuspiciousUserUpdateHandler.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.SuspiciousUser +{ + /// + /// Handler for 'channel.suspicious_user.update' notifications + /// + public class ChannelSuspiciousUserUpdateHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.suspicious_user.update"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelSuspiciousUserUpdate", new ChannelSuspiciousUserUpdateArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Vips/ChannelVipAddHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Vips/ChannelVipAddHandler.cs new file mode 100644 index 0000000..1b11576 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Vips/ChannelVipAddHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Vips +{ + /// + /// Handler for 'channel.vip.add' notifications + /// + public class ChannelVipAddHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.vip.add"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelVipAdd", new ChannelVipArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Vips/ChannelVipRemoveHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Vips/ChannelVipRemoveHandler.cs new file mode 100644 index 0000000..34bb979 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Vips/ChannelVipRemoveHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Vips +{ + /// + /// Handler for 'channel.vip.remove' notifications + /// + public class ChannelVipRemoveHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.vip.remove"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelVipRemove", new ChannelVipArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} \ No newline at end of file diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Warning/ChannelWarningAcknowledgeHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Warning/ChannelWarningAcknowledgeHandler.cs new file mode 100644 index 0000000..b6787f5 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Warning/ChannelWarningAcknowledgeHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Warning +{ + /// + /// Handler for 'channel.warning.acknowledge' notifications + /// + public class ChannelWarningAcknowledgeHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.warning.acknowledge"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelWarningAcknowledge", new ChannelWarningAcknowledgeArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/Channel/Warning/ChannelWarningSendHandler.cs b/TwitchLib.EventSub.Websockets/Handler/Channel/Warning/ChannelWarningSendHandler.cs new file mode 100644 index 0000000..236d9c9 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/Channel/Warning/ChannelWarningSendHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.Channel; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.Channel; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.Channel.Warning +{ + /// + /// Handler for 'channel.warning.send' notifications + /// + public class ChannelWarningSendHandler : INotificationHandler + { + /// + public string SubscriptionType => "channel.warning.send"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("ChannelWarningSend", new ChannelWarningSendArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/User/UserUpdateHandler.cs b/TwitchLib.EventSub.Websockets/Handler/User/UserUpdateHandler.cs index 4bc5093..47a436e 100644 --- a/TwitchLib.EventSub.Websockets/Handler/User/UserUpdateHandler.cs +++ b/TwitchLib.EventSub.Websockets/Handler/User/UserUpdateHandler.cs @@ -33,5 +33,5 @@ public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerial client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); } } - } -} \ No newline at end of file + } +} diff --git a/TwitchLib.EventSub.Websockets/Handler/User/UserWhisperMessageHandler.cs b/TwitchLib.EventSub.Websockets/Handler/User/UserWhisperMessageHandler.cs new file mode 100644 index 0000000..24367f9 --- /dev/null +++ b/TwitchLib.EventSub.Websockets/Handler/User/UserWhisperMessageHandler.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using TwitchLib.EventSub.Core.SubscriptionTypes.User; +using TwitchLib.EventSub.Websockets.Core.EventArgs; +using TwitchLib.EventSub.Websockets.Core.EventArgs.User; +using TwitchLib.EventSub.Websockets.Core.Handler; +using TwitchLib.EventSub.Websockets.Core.Models; + +namespace TwitchLib.EventSub.Websockets.Handler.User; + +/// +/// Handler for 'user.whisper.message' notifications +/// +public class UserWhisperMessageHandler : INotificationHandler +{ + /// + public string SubscriptionType => "user.whisper.message"; + + /// + public void Handle(EventSubWebsocketClient client, string jsonString, JsonSerializerOptions serializerOptions) + { + try + { + var data = JsonSerializer.Deserialize>(jsonString.AsSpan(), serializerOptions); + + if (data is null) + throw new InvalidOperationException("Parsed JSON cannot be null!"); + + client.RaiseEvent("UserWhisperMessage", new UserWhisperMessageArgs { Notification = data }); + } + catch (Exception ex) + { + client.RaiseEvent("ErrorOccurred", new ErrorOccuredArgs { Exception = ex, Message = $"Error encountered while trying to handle {SubscriptionType} notification! Raw Json: {jsonString}" }); + } + } +} diff --git a/TwitchLib.EventSub.Websockets/TwitchLib.EventSub.Websockets.csproj b/TwitchLib.EventSub.Websockets/TwitchLib.EventSub.Websockets.csproj index 1ac360e..8daedb8 100644 --- a/TwitchLib.EventSub.Websockets/TwitchLib.EventSub.Websockets.csproj +++ b/TwitchLib.EventSub.Websockets/TwitchLib.EventSub.Websockets.csproj @@ -5,10 +5,10 @@ TwitchLib.EventSub.Websockets TwitchLib.EventSub.Websockets swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin - 0.5.0 + 0.6.0 $(VersionSuffix) - 0.5.0 - 0.5.0 + 0.6.0 + 0.6.0 EventSub Websockets (also known as EventSockets) Client Library https://cdn.syzuna-programs.de/images/twitchlib.png https://github.com/TwitchLib/TwitchLib.EventSub.Websockets @@ -17,21 +17,22 @@ MIT Copyright 2023 twitch library events eventsub websockets eventsockets c# csharp netstandard2.0 netstandard2.1 net6.0 net7.0 net8.0 - Updated TwitchLib.EventSub.Core for the latest SubscriptionType changes + Updated dependencies, enabled nullable, added new events (ChannelChatClear, ChannelChatClearUserMessages, ChannelChatMessageDelete, ChannelChatNotification, ChannelVipAdd, ChannelVipRemove, ChannelPointsAutomaticRewardRedemptionAdd, ChannelSuspiciousUserMessage, ChannelSuspiciousUserUpdate, ChannelWarningAcknowledge, ChannelWarningSend, UserWhisperMessage, ChannelSharedChatSessionBegin, ChannelSharedChatSessionUpdate, ChannelSharedChatSessionEnd, ChannelUnbanRequestCreate, ChannelUnbanRequestResolve) en-US disable - disable + enable True latest-all True README.md latest + 1701;1702;CA1003 - - + +