using System.Text.Json; using System.Text.Json.Serialization; using KfChatDotNetBot.Settings; using NLog; using StackExchange.Redis; namespace KfChatDotNetBot.Services; public class YouTubePubSub : IDisposable { private string _connectionString; private CancellationToken _ct; private Logger _logger = LogManager.GetCurrentClassLogger(); private ConnectionMultiplexer? _redis; private ISubscriber? _sub; public delegate void OnNewVideoEventHandler(object sender, YouTubePubSubNotificationModel data); public event OnNewVideoEventHandler? OnNewVideo; public YouTubePubSub(CancellationToken cancellationToken = default) { _ct = cancellationToken; _connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.YouTubePubSubRedisConnectionString).Result .Value ?? throw new InvalidOperationException("YouTube PubSub Redis connection string was not defined"); } public async Task Connect() { var channel = await SettingsProvider.GetValueAsync(BuiltIn.Keys.YouTubePubSubRedisChannel); if (channel.Value == null) { throw new InvalidOperationException("Redis channel was null"); } _redis = await ConnectionMultiplexer.ConnectAsync(_connectionString); _sub = _redis.GetSubscriber(); await _sub.SubscribeAsync(new RedisChannel(channel.Value, RedisChannel.PatternMode.Literal), PubSubMessageReceived); } public bool IsConnected() { if (_redis == null || _sub == null) return false; return _sub.IsConnected(); } private void PubSubMessageReceived(RedisChannel channel, RedisValue message) { try { var payload = JsonSerializer.Deserialize(message.ToString()); if (payload == null) { throw new InvalidOperationException("Caught a null when attempting to deserialize the PubSub JSON"); } OnNewVideo?.Invoke(this, payload); } catch (Exception e) { _logger.Error("PubSub shit itself when trying to handle a message"); _logger.Error(e); _logger.Error("--- Payload ---"); _logger.Error(message.ToString()); _logger.Error("---------------"); } } public void Dispose() { _sub?.UnsubscribeAll(); _redis?.Dispose(); GC.SuppressFinalize(this); } } public class YouTubePubSubNotificationModel { [JsonPropertyName("id")] public required string Id { get; set; } [JsonPropertyName("title")] public required string Title { get; set; } [JsonPropertyName("url")] public required string Url { get; set; } }