Added missing websocket support for Redemptions and Actions. Fixed Ad Break actions. Cleaned some code.

This commit is contained in:
Tom 2025-01-07 15:30:13 +00:00
parent 77b37f04b6
commit 64cb0c1f6d
17 changed files with 227 additions and 36 deletions

View File

@ -1,4 +1,3 @@
using System.Net.WebSockets;
using System.Text.Json;
using System.Timers;
using CommonSocketLibrary.Abstract;
@ -9,6 +8,7 @@ using HermesSocketLibrary.Requests.Messages;
using HermesSocketLibrary.Socket.Data;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using TwitchChatTTS.Bus;
using TwitchChatTTS.Hermes.Socket.Handlers;
namespace TwitchChatTTS.Hermes.Socket
@ -19,6 +19,7 @@ namespace TwitchChatTTS.Hermes.Socket
private readonly User _user;
private readonly Configuration _configuration;
private readonly ServiceBusCentral _bus;
private readonly ICallbackManager<HermesRequestData> _callbackManager;
private string URL;
@ -33,10 +34,13 @@ namespace TwitchChatTTS.Hermes.Socket
public bool LoggedIn { get; set; }
public bool Ready { get; set; }
private bool _attempting;
public HermesSocketClient(
User user,
Configuration configuration,
ServiceBusCentral bus,
ICallbackManager<HermesRequestData> callbackManager,
[FromKeyedServices("hermes")] IBackoff backoff,
[FromKeyedServices("hermes")] IEnumerable<IWebSocketHandler> handlers,
@ -50,6 +54,7 @@ namespace TwitchChatTTS.Hermes.Socket
{
_user = user;
_configuration = configuration;
_bus = bus;
_callbackManager = callbackManager;
_backoff = backoff;
_heartbeatTimer = new System.Timers.Timer(TimeSpan.FromSeconds(15));
@ -59,6 +64,20 @@ namespace TwitchChatTTS.Hermes.Socket
URL = $"wss://{BASE_URL}";
_lock = new object();
var ttsCreateUserVoice = _bus.GetTopic("tts.user.voice.create");
ttsCreateUserVoice.Subscribe(new ServiceBusObserver(async data => await Send(3, new RequestMessage()
{
Type = "create_tts_user",
Data = (IDictionary<string, object>) data.Value!
}), logger));
var ttsUpdateUserVoice = _bus.GetTopic("tts.user.voice.update");
ttsUpdateUserVoice.Subscribe(new ServiceBusObserver(async data => await Send(3, new RequestMessage()
{
Type = "update_tts_user",
Data = (IDictionary<string, object>) data.Value!
}), logger));
}
@ -66,23 +85,29 @@ namespace TwitchChatTTS.Hermes.Socket
{
lock (_lock)
{
if (Connected)
if (Connected || _attempting)
return;
_attempting = true;
}
_logger.Debug($"Attempting to connect to {URL}");
await ConnectAsync(URL);
_attempting = false;
}
private async Task Disconnect()
{
lock (_lock)
{
if (!Connected)
if (!Connected || _attempting)
return;
_attempting = true;
}
await DisconnectAsync(new SocketDisconnectionEventArgs("Normal disconnection", "Disconnection was executed"));
_attempting = false;
}
public async Task CreateTTSVoice(string voiceName)

View File

@ -0,0 +1,35 @@
using System.Text.Json;
using HermesSocketLibrary.Requests.Messages;
using Serilog;
using TwitchChatTTS.Twitch.Redemptions;
namespace TwitchChatTTS.Hermes.Socket.Requests
{
public class CreateRedemptionAck : IRequestAck
{
public string Name => "create_redemption";
private readonly IRedemptionManager _redemptions;
private readonly JsonSerializerOptions _options;
private readonly ILogger _logger;
public CreateRedemptionAck(IRedemptionManager redemptions, JsonSerializerOptions options, ILogger logger)
{
_redemptions = redemptions;
_options = options;
_logger = logger;
}
public void Acknowledge(string requestId, string json, IDictionary<string, object>? requestData)
{
var redemption = JsonSerializer.Deserialize<Redemption>(json, _options);
if (redemption == null)
{
_logger.Warning($"Redemption data received is null.");
return;
}
_redemptions.Add(redemption);
_logger.Information($"A new redemption has been created [redemption id: {redemption.Id}][twitch redemption id: {redemption.TwitchRedemptionId}]");
}
}
}

View File

@ -21,7 +21,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
_logger.Warning("Request data is null.");
return;
}
if (!long.TryParse(requestData["chatter"].ToString(), out long chatterId))
{
_logger.Warning($"Failed to parse chatter id [chatter id: {requestData["chatter"]}]");

View File

@ -21,7 +21,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
_logger.Warning("Request data is null.");
return;
}
var voice = requestData["voice"].ToString()!;
var voiceId = json;
if (string.IsNullOrEmpty(voice))

View File

@ -0,0 +1,39 @@
using Serilog;
using TwitchChatTTS.Twitch.Redemptions;
namespace TwitchChatTTS.Hermes.Socket.Requests
{
public class DeleteRedeemableActionAck : IRequestAck
{
public string Name => "delete_redeemable_action";
private readonly IRedemptionManager _redemptions;
private readonly ILogger _logger;
public DeleteRedeemableActionAck(IRedemptionManager redemptions, ILogger logger)
{
_redemptions = redemptions;
_logger = logger;
}
public void Acknowledge(string requestId, string json, IDictionary<string, object>? requestData)
{
if (requestData == null)
{
_logger.Warning("Request data is null.");
return;
}
var name = requestData["name"].ToString();
if (string.IsNullOrEmpty(name))
{
_logger.Warning($"Action name is invalid [action name: {name}]");
return;
}
if (_redemptions.RemoveAction(name))
_logger.Information($"Deleted a redeemable action [action name: {name}]");
else
_logger.Warning($"Failed to delete a redeemable action [action name: {name}]");
}
}
}

View File

@ -0,0 +1,39 @@
using Serilog;
using TwitchChatTTS.Twitch.Redemptions;
namespace TwitchChatTTS.Hermes.Socket.Requests
{
public class DeleteRedemptionAck : IRequestAck
{
public string Name => "delete_redemption";
private readonly IRedemptionManager _redemptions;
private readonly ILogger _logger;
public DeleteRedemptionAck(IRedemptionManager redemptions, ILogger logger)
{
_redemptions = redemptions;
_logger = logger;
}
public void Acknowledge(string requestId, string json, IDictionary<string, object>? requestData)
{
if (requestData == null)
{
_logger.Warning("Request data is null.");
return;
}
var id = requestData["id"].ToString();
if (string.IsNullOrEmpty(id))
{
_logger.Warning($"Redemption Id is invalid [redemption id: {id}]");
return;
}
if (_redemptions.RemoveRedemption(id))
_logger.Information($"Deleted a redemption [redemption id: {id}]");
else
_logger.Warning($"Failed to delete a redemption [redemption id: {id}]");
}
}
}

View File

@ -21,7 +21,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
_logger.Warning("Request data is null.");
return;
}
var id = requestData["id"].ToString();
if (string.IsNullOrEmpty(id))
{

View File

@ -21,7 +21,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
_logger.Warning("Request data is null.");
return;
}
var voice = requestData["voice"].ToString();
if (string.IsNullOrEmpty(voice))
{

View File

@ -46,7 +46,8 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
}
_logger.Information($"Redeemable actions loaded [count: {actions.Count()}]");
_bus.Send(this, "redemptions_initiation", new RedemptionInitiation() {
_bus.Send(this, "redemptions_initiation", new RedemptionInitiation()
{
Redemptions = redemptions,
Actions = actions.ToDictionary(a => a.Name, a => a)
});

View File

@ -27,7 +27,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
var temp = new ConcurrentDictionary<long, string>();
foreach (var entry in users)
temp.TryAdd(entry.Key, entry.Value);
_user.VoicesSelected = temp;
_logger.Information($"Updated chatters' selected voice [count: {temp.Count()}]");
}

View File

@ -13,7 +13,7 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
_logger = logger;
}
public void Fulfill(string type, string requestId, string? data, IDictionary<string, object>? requestData)
public void Fulfill(string type, string requestId, string data, IDictionary<string, object>? requestData)
{
if (!_acknowledgements.TryGetValue(type, out var ack))
{

View File

@ -28,8 +28,10 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
return;
}
_redemptions.Update(action);
_logger.Information($"A new redeemable action has been created [action name: {action.Name}]");
if (_redemptions.Update(action))
_logger.Information($"A redeemable action has been updated [action name: {action.Name}]");
else
_logger.Warning($"Failed to update an existing redeemable action [action name: {action.Name}]");
}
}
}

View File

@ -0,0 +1,37 @@
using System.Text.Json;
using HermesSocketLibrary.Requests.Messages;
using Serilog;
using TwitchChatTTS.Twitch.Redemptions;
namespace TwitchChatTTS.Hermes.Socket.Requests
{
public class UpdateRedemptionAck : IRequestAck
{
public string Name => "update_redemption";
private readonly IRedemptionManager _redemptions;
private readonly JsonSerializerOptions _options;
private readonly ILogger _logger;
public UpdateRedemptionAck(IRedemptionManager redemptions, JsonSerializerOptions options, ILogger logger)
{
_redemptions = redemptions;
_options = options;
_logger = logger;
}
public void Acknowledge(string requestId, string json, IDictionary<string, object>? requestData)
{
var redemption = JsonSerializer.Deserialize<Redemption>(json, _options);
if (redemption == null)
{
_logger.Warning($"Redemption data received is null.");
return;
}
if (_redemptions.Update(redemption))
_logger.Information($"A redemption has been updated [redemption id: {redemption.Id}][twitch redemption id: {redemption.TwitchRedemptionId}]");
else
_logger.Warning($"Failed to update an existing redemption [redemption id: {redemption.Id}][twitch redemption id: {redemption.TwitchRedemptionId}]");
}
}
}

View File

@ -26,14 +26,15 @@ namespace TwitchChatTTS.Hermes.Socket.Requests
_logger.Warning($"TTS Filter data failed: null");
return;
}
_logger.Debug($"Filter data [filter id: {filter.Id}][search: {filter.Search}][group id: {filter.Replace}]");
var previous = _user.RegexFilters.FirstOrDefault(f => f.Id == filter.Id);
if (previous == null) {
if (previous == null)
{
_logger.Warning($"TTS Filter doest exist by id [filter id: {filter.Id}]");
return;
}
previous.Search = filter.Search;
previous.Replace = filter.Replace;
_logger.Information($"Filter has been updated [filter id: {filter.Id}]");

View File

@ -88,9 +88,15 @@ s.AddTransient<ICommandFactory, CommandFactory>();
// Request acks
s.AddSingleton<RequestAckManager>();
s.AddTransient<IRequestAck, CreatePolicyAck>();
s.AddTransient<IRequestAck, CreateRedeemableActionAck>();
s.AddTransient<IRequestAck, CreateRedemptionAck>();
s.AddTransient<IRequestAck, CreateTTSFilterAck>();
s.AddTransient<IRequestAck, CreateTTSUserAck>();
s.AddTransient<IRequestAck, CreateTTSVoiceAck>();
s.AddTransient<IRequestAck, DeletePolicyAck>();
s.AddTransient<IRequestAck, DeleteRedeemableActionAck>();
s.AddTransient<IRequestAck, DeleteRedemptionAck>();
s.AddTransient<IRequestAck, DeleteTTSFilterAck>();
s.AddTransient<IRequestAck, DeleteTTSVoiceAck>();
s.AddTransient<IRequestAck, GetChatterIdsAck>();
s.AddTransient<IRequestAck, GetConnectionsAck>();
@ -106,6 +112,9 @@ s.AddTransient<IRequestAck, GetTTSVoicesAck>();
s.AddTransient<IRequestAck, GetTTSWordFiltersAck>();
s.AddTransient<IRequestAck, UpdateDefaultTTSVoiceAck>();
s.AddTransient<IRequestAck, UpdatePolicyAck>();
s.AddTransient<IRequestAck, UpdateRedeemableActionAck>();
s.AddTransient<IRequestAck, UpdateRedemptionAck>();
s.AddTransient<IRequestAck, UpdateTTSFilterAck>();
s.AddTransient<IRequestAck, UpdateTTSUserAck>();
s.AddTransient<IRequestAck, UpdateTTSVoiceAck>();
s.AddTransient<IRequestAck, UpdateTTSVoiceStateAck>();

View File

@ -7,7 +7,6 @@ using org.mariuszgromada.math.mxparser;
using Serilog;
using TwitchChatTTS.Bus;
using TwitchChatTTS.Bus.Data;
using TwitchChatTTS.Hermes.Socket;
using TwitchChatTTS.OBS.Socket;
using TwitchChatTTS.OBS.Socket.Data;
using TwitchChatTTS.Veadotube;
@ -23,7 +22,6 @@ namespace TwitchChatTTS.Twitch.Redemptions
private readonly ServiceBusCentral _bus;
private readonly User _user;
private readonly OBSSocketClient _obs;
private readonly HermesSocketClient _hermes;
private readonly VeadoSocketClient _veado;
private readonly NightbotApiClient _nightbot;
private readonly AudioPlaybackEngine _playback;
@ -36,7 +34,6 @@ namespace TwitchChatTTS.Twitch.Redemptions
ServiceBusCentral bus,
User user,
[FromKeyedServices("obs")] SocketClient<WebSocketMessage> obs,
[FromKeyedServices("hermes")] SocketClient<WebSocketMessage> hermes,
[FromKeyedServices("veadotube")] SocketClient<object> veado,
NightbotApiClient nightbot,
AudioPlaybackEngine playback,
@ -48,7 +45,6 @@ namespace TwitchChatTTS.Twitch.Redemptions
_bus = bus;
_user = user;
_obs = (obs as OBSSocketClient)!;
_hermes = (hermes as HermesSocketClient)!;
_veado = (veado as VeadoSocketClient)!;
_nightbot = nightbot;
_playback = playback;
@ -284,12 +280,12 @@ namespace TwitchChatTTS.Twitch.Redemptions
if (_user.VoicesSelected.ContainsKey(senderId))
{
await _hermes.UpdateTTSUser(senderId, voiceId);
_bus.Send(this, "tts.user.voice.update", new Dictionary<string, object>() { { "chatter", senderId }, { "voice", voiceId } });
_logger.Debug($"Sent request to update chat TTS voice [voice: {voiceName}][chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]");
}
else
{
await _hermes.CreateTTSUser(senderId, voiceId);
_bus.Send(this, "tts.user.voice.create", new Dictionary<string, object>() { { "chatter", senderId }, { "voice", voiceId } });
_logger.Debug($"Sent request to create chat TTS voice [voice: {voiceName}][chatter id: {senderId}][source: redemption][chatter: {senderDisplayName}][chatter id: {senderId}]");
}
break;

View File

@ -25,14 +25,14 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
if (message.IsAutomatic)
_logger.Information($"Ad break has begun [duration: {message.DurationSeconds} seconds][automatic: true]");
else
_logger.Information($"Ad break has begun [duration: {message.DurationSeconds} seconds][requester: {message.RequesterUserLogin}][requester id: {message.RequesterUserId}]");
_logger.Information($"Ad break has begun [duration: {message.DurationSeconds} seconds][automatic: false][requester: {message.RequesterUserLogin}][requester id: {message.RequesterUserId}]");
try
{
var actions = _redemptionManager.Get("adbreak_begin");
if (!actions.Any())
if (actions.Any())
{
_logger.Debug($"Found {actions.Count()} actions for this Twitch ad break");
_logger.Debug($"Found {actions.Count()} actions for this Twitch ad break begin");
foreach (var action in actions)
try
@ -46,20 +46,27 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
}
else
_logger.Debug($"No redeemable actions for ad break begin was found");
}
catch (Exception ex)
{
_logger.Error(ex, $"Failed to fetch the redeemable actions for ad break begin");
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () =>
Task.Run(async () =>
{
try
{
await Task.Delay(TimeSpan.FromSeconds(message.DurationSeconds));
if (message.IsAutomatic)
_logger.Information($"Ad break has ended [duration: {message.DurationSeconds} seconds][automatic: true]");
else
_logger.Information($"Ad break has ended [duration: {message.DurationSeconds} seconds][requester: {message.RequesterUserLogin}][requester id: {message.RequesterUserId}]");
actions = _redemptionManager.Get("adbreak_end");
if (!actions.Any())
_logger.Information($"Ad break has ended [duration: {message.DurationSeconds} seconds][automatic: false][requester: {message.RequesterUserLogin}][requester id: {message.RequesterUserId}]");
var actions = _redemptionManager.Get("adbreak_end");
if (actions.Any())
{
_logger.Debug($"Found {actions.Count()} actions for this Twitch ad break");
_logger.Debug($"Found {actions.Count()} actions for this Twitch ad break end");
foreach (var action in actions)
try
@ -73,13 +80,13 @@ namespace TwitchChatTTS.Twitch.Socket.Handlers
}
else
_logger.Debug($"No redeemable actions for ad break end was found");
});
}
catch (Exception ex)
{
_logger.Error(ex, $"Failed to fetch the redeemable actions for ad break end");
}
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
catch (Exception ex)
{
_logger.Error(ex, $"Failed to fetch the redeemable actions for ad break begin");
}
}
}
}