Changed command dictionary to a command tree. Fixed various requests. OBS reconnection added if identified previously.

This commit is contained in:
Tom
2024-07-19 16:56:41 +00:00
parent e6b3819356
commit 472bfcee5d
56 changed files with 1943 additions and 1553 deletions

View File

@ -1,7 +0,0 @@
namespace TwitchChatTTS.Seven.Socket.Context
{
public class ReconnectContext
{
public string? SessionId;
}
}

View File

@ -1,102 +1,21 @@
using System.Net.WebSockets;
using CommonSocketLibrary.Abstract;
using CommonSocketLibrary.Common;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using TwitchChatTTS.Seven.Socket.Context;
using TwitchChatTTS.Seven.Socket.Data;
namespace TwitchChatTTS.Seven.Socket.Handlers
{
public class EndOfStreamHandler : IWebSocketHandler
{
private readonly ILogger _logger;
private readonly User _user;
private readonly IServiceProvider _serviceProvider;
private readonly string[] _errorCodes;
private readonly int[] _reconnectDelay;
public int OperationCode { get; } = 7;
public EndOfStreamHandler(User user, IServiceProvider serviceProvider, ILogger logger)
{
_logger = logger;
_user = user;
_serviceProvider = serviceProvider;
_errorCodes = [
"Server Error",
"Unknown Operation",
"Invalid Payload",
"Auth Failure",
"Already Identified",
"Rate Limited",
"Restart",
"Maintenance",
"Timeout",
"Already Subscribed",
"Not Subscribed",
"Insufficient Privilege",
"Inactivity?"
];
_reconnectDelay = [
1000,
-1,
-1,
-1,
0,
3000,
1000,
300000,
1000,
0,
0,
1000,
1000
];
}
public async Task Execute<Data>(SocketClient<WebSocketMessage> sender, Data data)
{
if (data is not EndOfStreamMessage message || message == null)
return;
var code = message.Code - 4000;
if (code >= 0 && code < _errorCodes.Length)
_logger.Warning($"Received end of stream message (reason: {_errorCodes[code]}, code: {message.Code}, message: {message.Message}).");
else
_logger.Warning($"Received end of stream message (code: {message.Code}, message: {message.Message}).");
await sender.DisconnectAsync();
if (code >= 0 && code < _reconnectDelay.Length && _reconnectDelay[code] < 0)
{
_logger.Error($"7tv client will remain disconnected due to a bad client implementation.");
return;
}
if (string.IsNullOrWhiteSpace(_user.SevenEmoteSetId))
{
_logger.Warning("Could not find the 7tv emote set id. Not reconnecting.");
return;
}
var context = _serviceProvider.GetRequiredService<ReconnectContext>();
if (_reconnectDelay[code] > 0)
await Task.Delay(_reconnectDelay[code]);
var manager = _serviceProvider.GetRequiredService<SevenManager>();
await manager.Connect();
if (context.SessionId != null)
{
await sender.Send(34, new ResumeMessage() { SessionId = context.SessionId });
_logger.Debug("Resumed connection to 7tv websocket.");
}
else
{
_logger.Debug("Resumed connection to 7tv websocket on a different session.");
}
await sender.DisconnectAsync(new SocketDisconnectionEventArgs(WebSocketCloseStatus.Empty.ToString(), code.ToString()));
}
}
}

View File

@ -19,7 +19,6 @@ namespace TwitchChatTTS.Seven.Socket.Handlers
{
if (data is not SevenHelloMessage message || message == null)
return;
if (sender is not SevenSocketClient seven || seven == null)
return;

View File

@ -1,41 +0,0 @@
using Serilog;
using CommonSocketLibrary.Socket.Manager;
using CommonSocketLibrary.Common;
using Microsoft.Extensions.DependencyInjection;
namespace TwitchChatTTS.Seven.Socket.Managers
{
public class SevenHandlerManager : WebSocketHandlerManager
{
public SevenHandlerManager(ILogger logger, IServiceProvider provider) : base(logger)
{
try
{
var basetype = typeof(IWebSocketHandler);
var assembly = GetType().Assembly;
var types = assembly.GetTypes().Where(t => t.IsClass && basetype.IsAssignableFrom(t) && t.AssemblyQualifiedName?.Contains(".Seven.") == true);
foreach (var type in types)
{
var key = "7tv-" + type.Name.Replace("Handlers", "Hand#lers")
.Replace("Handler", "")
.Replace("Hand#lers", "Handlers")
.ToLower();
var handler = provider.GetKeyedService<IWebSocketHandler>(key);
if (handler == null)
{
logger.Error("Failed to find 7tv websocket handler: " + type.AssemblyQualifiedName);
continue;
}
_logger.Debug($"Linked type {type.AssemblyQualifiedName} to 7tv websocket handler {handler.GetType().AssemblyQualifiedName}.");
Add(handler);
}
}
catch (Exception e)
{
_logger.Error(e, "Failed to load 7tv websocket handler types.");
}
}
}
}

View File

@ -1,4 +1,3 @@
using CommonSocketLibrary.Abstract;
using CommonSocketLibrary.Common;
using CommonSocketLibrary.Socket.Manager;
using Microsoft.Extensions.DependencyInjection;
@ -6,13 +5,12 @@ using Serilog;
namespace TwitchChatTTS.Seven.Socket.Managers
{
public class SevenHandlerTypeManager : WebSocketHandlerTypeManager
public class SevenMessageTypeManager : WebSocketMessageTypeManager
{
public SevenHandlerTypeManager(
ILogger factory,
[FromKeyedServices("7tv")] HandlerManager<WebSocketClient,
IWebSocketHandler> handlers
) : base(factory, handlers)
public SevenMessageTypeManager(
[FromKeyedServices("7tv")] IEnumerable<IWebSocketHandler> handlers,
ILogger logger
) : base(handlers, logger)
{
}
}

View File

@ -9,19 +9,133 @@ namespace TwitchChatTTS.Seven.Socket
{
public class SevenSocketClient : WebSocketClient
{
private readonly User _user;
private readonly string[] _errorCodes;
private readonly int[] _reconnectDelay;
private string? URL;
public bool Connected { get; set; }
public SevenHelloMessage? ConnectionDetails { get; set; }
public SevenSocketClient(
ILogger logger,
[FromKeyedServices("7tv")] HandlerManager<WebSocketClient, IWebSocketHandler> handlerManager,
[FromKeyedServices("7tv")] HandlerTypeManager<WebSocketClient, IWebSocketHandler> typeManager
) : base(logger, handlerManager, typeManager, new JsonSerializerOptions()
User user,
[FromKeyedServices("7tv")] IEnumerable<IWebSocketHandler> handlers,
[FromKeyedServices("7tv")] MessageTypeManager<IWebSocketHandler> typeManager,
ILogger logger
) : base(handlers, typeManager, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = false,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
})
}, logger)
{
_user = user;
ConnectionDetails = null;
_errorCodes = [
"Server Error",
"Unknown Operation",
"Invalid Payload",
"Auth Failure",
"Already Identified",
"Rate Limited",
"Restart",
"Maintenance",
"Timeout",
"Already Subscribed",
"Not Subscribed",
"Insufficient Privilege",
"Inactivity?"
];
_reconnectDelay = [
1000,
-1,
-1,
-1,
0,
3000,
1000,
300000,
1000,
0,
0,
1000,
1000
];
}
public void Initialize()
{
_logger.Information("Initializing 7tv websocket client.");
OnConnected += (sender, e) =>
{
Connected = true;
_logger.Information("7tv websocket client connected.");
};
OnDisconnected += (sender, e) => OnDisconnection(sender, e);
if (!string.IsNullOrEmpty(_user.SevenEmoteSetId))
URL = $"{SevenApiClient.WEBSOCKET_URL}@emote_set.*<object_id={_user.SevenEmoteSetId}>";
}
public async Task Connect()
{
if (string.IsNullOrEmpty(URL))
{
_logger.Warning("Cannot find 7tv url. Not connecting to 7tv websockets.");
return;
}
if (string.IsNullOrWhiteSpace(_user.SevenEmoteSetId))
{
_logger.Warning("Cannot find 7tv data for your channel. Not connecting to 7tv websockets.");
return;
}
_logger.Debug($"7tv client attempting to connect to {URL}");
await ConnectAsync($"{URL}");
}
private async void OnDisconnection(object? sender, SocketDisconnectionEventArgs e)
{
Connected = false;
if (int.TryParse(e.Reason, out int code))
{
if (code >= 0 && code < _errorCodes.Length)
_logger.Warning($"Received end of stream message for 7tv websocket [reason: {_errorCodes[code]}][code: {code}]");
else
_logger.Warning($"Received end of stream message for 7tv websocket [code: {code}]");
if (code >= 0 && code < _reconnectDelay.Length && _reconnectDelay[code] < 0)
{
_logger.Error($"7tv client will remain disconnected due to a bad client implementation.");
return;
}
if (_reconnectDelay[code] > 0)
await Task.Delay(_reconnectDelay[code]);
}
if (string.IsNullOrWhiteSpace(_user.SevenEmoteSetId))
{
_logger.Warning("Could not find the 7tv emote set id. Not reconnecting.");
return;
}
await Connect();
await Task.Delay(TimeSpan.FromMilliseconds(500));
if (Connected && ConnectionDetails?.SessionId != null)
{
await Send(34, new ResumeMessage() { SessionId = ConnectionDetails.SessionId });
_logger.Debug("Resumed connection to 7tv websocket.");
}
else
{
_logger.Debug("Resumed connection to 7tv websocket on a different session.");
}
}
}
}