using HermesSocketLibrary.db; using HermesSocketLibrary.Requests.Messages; using HermesSocketLibrary.Socket.Data; using HermesSocketServer.Services; using HermesSocketServer.Store; using ILogger = Serilog.ILogger; namespace HermesSocketServer.Socket.Handlers { public class HermesLoginHandler : ISocketHandler { public int OperationCode { get; } = 1; private readonly ChannelManager _manager; private readonly IStore _voices; private readonly ServerConfiguration _configuration; private readonly Database _database; private readonly HermesSocketManager _sockets; private readonly ILogger _logger; private readonly SemaphoreSlim _semaphore; public HermesLoginHandler(ChannelManager manager, IStore voices, ServerConfiguration configuration, Database database, HermesSocketManager sockets, ILogger logger) { _manager = manager; _voices = voices; _configuration = configuration; _database = database; _sockets = sockets; _logger = logger; _semaphore = new SemaphoreSlim(1); } public async Task Execute(WebSocketUser sender, T message, HermesSocketManager sockets) { if (message is not HermesLoginMessage data || data == null || data.ApiKey == null) return; if (sender.Id != null) return; string sql = "select \"userId\" from \"ApiKey\" where id = @key"; var result = await _database.ExecuteScalar(sql, new Dictionary() { { "key", data.ApiKey } }); string? userId = result?.ToString(); if (string.IsNullOrWhiteSpace(userId)) return; IEnumerable recipients = Enumerable.Empty(); try { await _semaphore.WaitAsync(); if (sender.Id != null) return; sender.Id = userId; sender.Slave = data.WebLogin || recipients.Where(r => r != null && !r.WebLogin).Any(); recipients = _sockets.GetSockets(userId).ToList().Where(s => s.SessionId != sender.SessionId); sender.ApiKey = data.ApiKey; sender.WebLogin = data.WebLogin; // Fetch channel data. var channel = _manager.Get(userId); if (channel == null) { channel = await _manager.Add(userId); if (channel == null) throw new Exception("Channel does not exist."); } sender.Name = channel.User.Name; sender.Admin = channel.User.Role == "ADMIN"; if (string.IsNullOrEmpty(sender.Name)) { _logger.Error($"Could not find username for a certain user [user id: {userId}][api key: {data.ApiKey}]"); return; } sql = "select \"providerAccountId\" from \"Account\" where \"userId\" = @user and provider = @provider"; var result2 = await _database.ExecuteScalar(sql, new Dictionary() { { "user", userId }, { "provider", "twitch" } }); var providerId = result2?.ToString(); if (providerId == null) { _logger.Warning($"Could not find the Provider Account Id [user id: {userId}][provider: twitch]"); return; } var voices = _voices.Get(); var ack = new LoginAckMessage() { UserId = userId, ProviderAccountId = providerId, SessionId = sender.SessionId, UserName = channel.User.Name, OwnerId = _configuration.Tts.OwnerId, Admin = sender.Admin, WebLogin = data.WebLogin, WordFilters = channel.Filters.Get().Values, DefaultTTSVoice = channel.User.DefaultVoice ?? _configuration.Tts.DefaultTtsVoice, TTSVoicesAvailable = _voices.Get().ToDictionary(v => v.Key, v => v.Value.Name), EnabledTTSVoices = channel.VoiceStates.Get().Values.Where(v => v.Enabled && voices.ContainsKey(v.Id)).Select(v => voices[v.Id].Name).ToList(), Connections = channel.Connections.Get().Values.ToList(), Slave = sender.Slave, }; await sender.Send(2, ack); // Sending notification to other clients about another client logging in. string version = $"{data.MajorVersion}.{data.MinorVersion}.{data.PatchVersion}"; _logger.Information($"Hermes client logged in {(sender.Admin ? "as administrator " : "")}[name: {sender.Name}][id: {userId}][ip: {sender.IPAddress}][version: {version}][web: {data.WebLogin}]"); ack = new LoginAckMessage() { AnotherClient = true, UserId = userId, OwnerId = _configuration.Tts.OwnerId, WebLogin = data.WebLogin }; var tasks = new List(); foreach (var socket in recipients) { try { tasks.Add(socket!.Send(2, ack)); } catch (Exception) { } } await Task.WhenAll(tasks); } finally { _semaphore.Release(); } } } }