diff --git a/Chat/Commands/VersionCommand.cs b/Chat/Commands/VersionCommand.cs index 624ea8b..8ecd9c0 100644 --- a/Chat/Commands/VersionCommand.cs +++ b/Chat/Commands/VersionCommand.cs @@ -1,7 +1,6 @@ using HermesSocketLibrary.Socket.Data; using Serilog; using TwitchChatTTS.Hermes.Socket; -using TwitchChatTTS.Twitch.Socket; using TwitchChatTTS.Twitch.Socket.Messages; using static TwitchChatTTS.Chat.Commands.TTSCommands; @@ -40,9 +39,9 @@ namespace TwitchChatTTS.Chat.Commands public async Task Execute(IDictionary values, ChannelChatMessage message, HermesSocketClient hermes) { - _logger.Information($"TTS Version: {TTS.MAJOR_VERSION}.{TTS.MINOR_VERSION}"); + _logger.Information($"TTS Version: {TTS.MAJOR_VERSION}.{TTS.MINOR_VERSION}.{TTS.PATCH_VERSION}"); - await hermes.SendLoggingMessage(HermesLoggingLevel.Info, $"{_user.TwitchUsername} [twitch id: {_user.TwitchUserId}] using version {TTS.MAJOR_VERSION}.{TTS.MINOR_VERSION}."); + await hermes.SendLoggingMessage(HermesLoggingLevel.Info, $"{_user.TwitchUsername} [twitch id: {_user.TwitchUserId}] using version {TTS.MAJOR_VERSION}.{TTS.MINOR_VERSION}.{TTS.PATCH_VERSION}."); } } } diff --git a/Chat/Messaging/ChatMessageReader.cs b/Chat/Messaging/ChatMessageReader.cs index 4703a5b..ba7719b 100644 --- a/Chat/Messaging/ChatMessageReader.cs +++ b/Chat/Messaging/ChatMessageReader.cs @@ -62,7 +62,7 @@ namespace TwitchChatTTS.Chat.Messaging var emoteUsage = GetEmoteUsage(fragments); var tasks = new List(); - if (_obs.Streaming && _configuration.Twitch?.Slave != true) + if (_obs.Streaming && !_user.Slave) { if (emoteUsage.NewEmotes.Any()) tasks.Add(_hermes.SendEmoteDetails(emoteUsage.NewEmotes)); diff --git a/Configuration.cs b/Configuration.cs index 6361794..fe3c5f5 100644 --- a/Configuration.cs +++ b/Configuration.cs @@ -15,7 +15,6 @@ namespace TwitchChatTTS public class TwitchConfiguration { public bool TtsWhenOffline; - public bool Slave; public string? WebsocketUrl; public string? ApiUrl; } diff --git a/Hermes/Socket/Handlers/LoggingHandler.cs b/Hermes/Socket/Handlers/LoggingHandler.cs new file mode 100644 index 0000000..e41af63 --- /dev/null +++ b/Hermes/Socket/Handlers/LoggingHandler.cs @@ -0,0 +1,45 @@ +using CommonSocketLibrary.Abstract; +using CommonSocketLibrary.Common; +using HermesSocketLibrary.Socket.Data; +using Serilog; + +namespace TwitchChatTTS.Hermes.Socket.Handlers +{ + public class LoggingHandler : IWebSocketHandler + { + private readonly ILogger _logger; + public int OperationCode { get; } = 5; + + public LoggingHandler(ILogger logger) + { + _logger = logger; + } + + public Task Execute(SocketClient sender, Data data) + { + if (data is not LoggingMessage message || message == null) + return Task.CompletedTask; + + Action logging; + if (message.Level == HermesLoggingLevel.Trace) + logging = _logger.Verbose; + else if (message.Level == HermesLoggingLevel.Debug) + logging = _logger.Debug; + else if (message.Level == HermesLoggingLevel.Info) + logging = _logger.Information; + else if (message.Level == HermesLoggingLevel.Warn) + logging = _logger.Warning; + else if (message.Level == HermesLoggingLevel.Error) + logging = _logger.Error; + else if (message.Level == HermesLoggingLevel.Critical) + logging = _logger.Fatal; + else { + _logger.Warning("Failed to receive a logging level from client."); + return Task.CompletedTask; + } + + logging.Invoke(message.Exception, message.Message); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Hermes/Socket/Handlers/LoginAckHandler.cs b/Hermes/Socket/Handlers/LoginAckHandler.cs index 277b32a..34f55c6 100644 --- a/Hermes/Socket/Handlers/LoginAckHandler.cs +++ b/Hermes/Socket/Handlers/LoginAckHandler.cs @@ -43,6 +43,9 @@ namespace TwitchChatTTS.Hermes.Socket.Handlers return; } + _user.Slave = message.Slave; + _logger.Information(_user.Slave ? "This client is not responsible for reacting to chat messages." : "This client is responsible for reacting to chat messages."); + _user.HermesUserId = message.UserId; _user.HermesUsername = message.UserName; _user.TwitchUsername = message.UserName; diff --git a/Hermes/Socket/Handlers/SlaveHandler.cs b/Hermes/Socket/Handlers/SlaveHandler.cs new file mode 100644 index 0000000..34cb9ae --- /dev/null +++ b/Hermes/Socket/Handlers/SlaveHandler.cs @@ -0,0 +1,30 @@ +using CommonSocketLibrary.Abstract; +using CommonSocketLibrary.Common; +using HermesSocketLibrary.Socket.Data; +using Serilog; + +namespace TwitchChatTTS.Hermes.Socket.Handlers +{ + public class SlaveHandler : IWebSocketHandler + { + private readonly User _user; + private readonly ILogger _logger; + public int OperationCode { get; } = 9; + + public SlaveHandler(User user, ILogger logger) + { + _user = user; + _logger = logger; + } + + public Task Execute(SocketClient sender, Data data) + { + if (data is not SlaveMessage message || message == null) + return Task.CompletedTask; + + _user.Slave = message.Slave; + _logger.Information(_user.Slave ? "Total chat message ownership was revoked." : "This client is now responsible for reacting to chat messages. Potential chat messages were missed while changing ownership."); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Hermes/Socket/HermesSocketClient.cs b/Hermes/Socket/HermesSocketClient.cs index 2d50c04..1922949 100644 --- a/Hermes/Socket/HermesSocketClient.cs +++ b/Hermes/Socket/HermesSocketClient.cs @@ -68,14 +68,14 @@ namespace TwitchChatTTS.Hermes.Socket ttsCreateUserVoice.Subscribe(async data => await Send(3, new RequestMessage() { Type = "create_tts_user", - Data = (IDictionary) data.Value! + Data = (IDictionary)data.Value! })); var ttsUpdateUserVoice = _bus.GetTopic("tts.user.voice.update"); ttsUpdateUserVoice.Subscribe(async data => await Send(3, new RequestMessage() { Type = "update_tts_user", - Data = (IDictionary) data.Value! + Data = (IDictionary)data.Value! })); } @@ -267,6 +267,7 @@ namespace TwitchChatTTS.Hermes.Socket ApiKey = _configuration.Hermes!.Token!, MajorVersion = TTS.MAJOR_VERSION, MinorVersion = TTS.MINOR_VERSION, + PatchVersion = TTS.PATCH_VERSION, }); }; @@ -276,11 +277,13 @@ namespace TwitchChatTTS.Hermes.Socket { if (!Connected) return; + Connected = false; + LoggedIn = false; + Ready = false; + _user.Slave = true; } - LoggedIn = false; - Ready = false; _logger.Warning("Tom to Speech websocket client disconnected."); _heartbeatTimer.Enabled = false; diff --git a/Hermes/Socket/Requests/CreateTTSUserAck.cs b/Hermes/Socket/Requests/CreateTTSUserAck.cs index 6878fe9..9561fe4 100644 --- a/Hermes/Socket/Requests/CreateTTSUserAck.cs +++ b/Hermes/Socket/Requests/CreateTTSUserAck.cs @@ -33,13 +33,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests return; } - var userId = requestData["user"].ToString(); var voiceId = requestData["voice"].ToString(); - if (string.IsNullOrEmpty(userId)) - { - _logger.Warning("User Id is invalid."); - return; - } if (string.IsNullOrEmpty(voiceId)) { _logger.Warning("Voice Id is invalid."); @@ -52,7 +46,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests } _user.VoicesSelected.Add(chatterId, voiceId); - _logger.Information($"Created a new TTS user [user id: {userId}][voice id: {voiceId}][voice name: {voiceName}]."); + _logger.Information($"Created a new TTS user [chatter id: {_user.TwitchUserId}][chatter id: {chatterId}][voice id: {voiceId}][voice name: {voiceName}]."); } } } \ No newline at end of file diff --git a/Hermes/Socket/Requests/GetPermissionsAck.cs b/Hermes/Socket/Requests/GetPermissionsAck.cs index 50b935e..7bfa144 100644 --- a/Hermes/Socket/Requests/GetPermissionsAck.cs +++ b/Hermes/Socket/Requests/GetPermissionsAck.cs @@ -54,7 +54,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests foreach (var permission in groupInfo.GroupPermissions) { _logger.Debug($"Adding group permission [permission id: {permission.Id}][group id: {permission.GroupId}][path: {permission.Path}][allow: {permission.Allow?.ToString() ?? "null"}]"); - if (!groupsById.TryGetValue(permission.GroupId, out var group)) + if (!groupsById.TryGetValue(permission.GroupId.ToString(), out var group)) { _logger.Warning($"Failed to find group by id [permission id: {permission.Id}][group id: {permission.GroupId}][path: {permission.Path}]"); continue; diff --git a/Hermes/Socket/Requests/UpdateTTSUserAck.cs b/Hermes/Socket/Requests/UpdateTTSUserAck.cs index e52f259..8a309e9 100644 --- a/Hermes/Socket/Requests/UpdateTTSUserAck.cs +++ b/Hermes/Socket/Requests/UpdateTTSUserAck.cs @@ -33,13 +33,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests return; } - var userId = requestData["user"].ToString(); var voiceId = requestData["voice"].ToString(); - if (string.IsNullOrEmpty(userId)) - { - _logger.Warning("User Id is invalid."); - return; - } if (string.IsNullOrEmpty(voiceId)) { _logger.Warning("Voice Id is invalid."); @@ -52,7 +46,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests } _user.VoicesSelected[chatterId] = voiceId; - _logger.Information($"Updated a TTS user's voice [user id: {userId}][voice: {voiceId}][voice name: {voiceName}]"); + _logger.Information($"Updated a TTS user's voice [user id: {_user.TwitchUserId}][voice: {voiceId}][voice name: {voiceName}]"); } } } \ No newline at end of file diff --git a/Hermes/TTSVersion.cs b/Hermes/TTSVersion.cs index 04779d0..30cc97a 100644 --- a/Hermes/TTSVersion.cs +++ b/Hermes/TTSVersion.cs @@ -4,6 +4,7 @@ namespace TwitchChatTTS.Hermes { public int MajorVersion { get; set; } public int MinorVersion { get; set; } + public int? PatchVersion { get; set; } public required string Download { get; set; } public required string Changelog { get; set; } } diff --git a/OBS/Socket/Data/HelloMessage.cs b/OBS/Socket/Data/HelloMessage.cs index 55bd4b8..cf30369 100644 --- a/OBS/Socket/Data/HelloMessage.cs +++ b/OBS/Socket/Data/HelloMessage.cs @@ -4,7 +4,7 @@ namespace TwitchChatTTS.OBS.Socket.Data { public required string ObsWebSocketVersion { get; set; } public int RpcVersion { get; set; } - public required AuthenticationMessage Authentication { get; set; } + public AuthenticationMessage? Authentication { get; set; } } public class AuthenticationMessage { diff --git a/Startup.cs b/Startup.cs index a631304..a8f1185 100644 --- a/Startup.cs +++ b/Startup.cs @@ -209,6 +209,8 @@ s.AddKeyedSingleton("hermes", new ExponentialBackoff(1000, 15 * 1000)) s.AddKeyedSingleton("hermes"); s.AddKeyedSingleton("hermes"); s.AddKeyedSingleton("hermes"); +s.AddKeyedSingleton("hermes"); +s.AddKeyedSingleton("hermes"); s.AddKeyedSingleton, HermesMessageTypeManager>("hermes"); s.AddKeyedSingleton, HermesSocketClient>("hermes"); diff --git a/TTS.cs b/TTS.cs index b275e1d..8996ba0 100644 --- a/TTS.cs +++ b/TTS.cs @@ -22,7 +22,8 @@ namespace TwitchChatTTS public class TTS : IHostedService { public const int MAJOR_VERSION = 4; - public const int MINOR_VERSION = 7; + public const int MINOR_VERSION = 8; + public const int PATCH_VERSION = 2; private readonly User _user; private readonly HermesApiClient _hermesApiClient; @@ -77,6 +78,9 @@ namespace TwitchChatTTS Console.Title = "TTS - Twitch Chat"; Console.OutputEncoding = Encoding.UTF8; License.iConfirmCommercialUse("abcdef"); + _user.Slave = true; + + _logger.Information($"This is running on version {MAJOR_VERSION}.{MINOR_VERSION}.{PATCH_VERSION}."); if (string.IsNullOrWhiteSpace(_configuration.Hermes?.Token)) { @@ -92,9 +96,9 @@ namespace TwitchChatTTS _logger.Error("Failed to fetch latest TTS version. Something went wrong."); return; } - if (hermesVersion.MajorVersion > TTS.MAJOR_VERSION || hermesVersion.MajorVersion == TTS.MAJOR_VERSION && hermesVersion.MinorVersion > TTS.MINOR_VERSION) + if (hermesVersion.MajorVersion > TTS.MAJOR_VERSION || hermesVersion.MajorVersion == TTS.MAJOR_VERSION && (hermesVersion.MinorVersion > TTS.MINOR_VERSION || hermesVersion.MinorVersion == TTS.MINOR_VERSION && (hermesVersion.PatchVersion == null || hermesVersion.PatchVersion > TTS.PATCH_VERSION))) { - _logger.Information($"A new update for TTS is avaiable! Version {hermesVersion.MajorVersion}.{hermesVersion.MinorVersion} is available at {hermesVersion.Download}"); + _logger.Information($"A new update for TTS is avaiable! Version {hermesVersion.MajorVersion}.{hermesVersion.MinorVersion}.{hermesVersion.PatchVersion} is available at {hermesVersion.Download}"); var changes = hermesVersion.Changelog.Split("\n"); if (changes != null && changes.Any()) _logger.Information("Changelog:\n - " + string.Join("\n - ", changes) + "\n\n"); @@ -168,7 +172,12 @@ namespace TwitchChatTTS { await Task.Delay(TimeSpan.FromSeconds(5)); if (_user.TwitchUserId == default) + { _logger.Warning("Ensure your Tom to Speech token in the tts.config.yml file is valid."); + _logger.Warning("Re-open the application once you have made sure the token is valid."); + } + + disposables.ForEach(d => d.Dispose()); }); } diff --git a/Twitch/Redemptions/RedemptionManager.cs b/Twitch/Redemptions/RedemptionManager.cs index d19eb53..3a06c67 100644 --- a/Twitch/Redemptions/RedemptionManager.cs +++ b/Twitch/Redemptions/RedemptionManager.cs @@ -99,7 +99,9 @@ namespace TwitchChatTTS.Twitch.Redemptions { _redemptions.Add(redemption.Id, redemption); _logger.Debug($"Added redemption to redemption manager [redemption id: {redemption.Id}]"); - } else { + } + else + { _redemptions[redemption.Id] = redemption; _logger.Debug($"Updated redemption to redemption manager [redemption id: {redemption.Id}]"); } @@ -263,6 +265,11 @@ namespace TwitchChatTTS.Twitch.Redemptions break; case "SPECIFIC_TTS_VOICE": case "RANDOM_TTS_VOICE": + if (_user.Slave) + { + _logger.Debug($"Ignoring channel redemption due to being a slave client [chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]"); + break; + } string voiceId = string.Empty; bool specific = action.Type == "SPECIFIC_TTS_VOICE"; @@ -327,18 +334,43 @@ namespace TwitchChatTTS.Twitch.Redemptions _logger.Debug($"Played an audio file for channel point redeem [file: {action.Data["file_path"]}][chatter: {senderDisplayName}][chatter id: {senderId}]"); break; case "NIGHTBOT_PLAY": + if (_user.Slave) + { + _logger.Debug($"Ignoring channel redemption due to being a slave client [chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]"); + break; + } await _nightbot.Play(); break; case "NIGHTBOT_PAUSE": + if (_user.Slave) + { + _logger.Debug($"Ignoring channel redemption due to being a slave client [chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]"); + break; + } await _nightbot.Pause(); break; case "NIGHTBOT_SKIP": + if (_user.Slave) + { + _logger.Debug($"Ignoring channel redemption due to being a slave client [chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]"); + break; + } await _nightbot.Skip(); break; case "NIGHTBOT_CLEAR_PLAYLIST": + if (_user.Slave) + { + _logger.Debug($"Ignoring channel redemption due to being a slave client [chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]"); + break; + } await _nightbot.ClearPlaylist(); break; case "NIGHTBOT_CLEAR_QUEUE": + if (_user.Slave) + { + _logger.Debug($"Ignoring channel redemption due to being a slave client [chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]"); + break; + } await _nightbot.ClearQueue(); break; case "VEADOTUBE_SET_STATE": diff --git a/Twitch/Socket/Handlers/ChannelChatMessageHandler.cs b/Twitch/Socket/Handlers/ChannelChatMessageHandler.cs index 8a98aa9..235150d 100644 --- a/Twitch/Socket/Handlers/ChannelChatMessageHandler.cs +++ b/Twitch/Socket/Handlers/ChannelChatMessageHandler.cs @@ -14,12 +14,12 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers public string Name => "channel.chat.message"; private readonly IChatMessageReader _reader; - private readonly User _user; private readonly ICommandManager _commands; private readonly IGroupPermissionManager _permissionManager; private readonly IUsagePolicy _permissionPolicy; private readonly IChatterGroupManager _chatterGroupManager; private readonly ServiceBusCentral _bus; + private readonly User _user; private readonly ILogger _logger; @@ -35,12 +35,12 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers ) { _reader = reader; - _user = user; _commands = commands; _permissionManager = permissionManager; _permissionPolicy = permissionPolicy; _chatterGroupManager = chatterGroupManager; _bus = bus; + _user = user; _logger = logger; } @@ -58,13 +58,13 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers var groups = GetGroups(message.Badges, chatterId); var bits = GetTotalBits(fragments); - if (message.ChannelPointsCustomRewardId == null) + if (message.ChannelPointsCustomRewardId == null && !_user.Slave) { var commandResult = await CheckForChatCommand(message.Message.Text, message, groups); if (commandResult != ChatCommandResult.Unknown) return; } - else + else if (message.ChannelPointsCustomRewardId != null) { _bus.Send(this, "chat_message_redemption", message); } diff --git a/User.cs b/User.cs index ed3504a..cda1e7e 100644 --- a/User.cs +++ b/User.cs @@ -33,6 +33,8 @@ namespace TwitchChatTTS [JsonIgnore] public Regex? VoiceNameRegex { get; set; } + public required bool Slave { get; set; } + private IDictionary _voicesAvailable; private HashSet _voicesEnabled;