using CommandLine; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Threading; using RedisManager.Utils; namespace RedisManager.Commands { /// /// Contains command line options and implementations for Redis Pub/Sub operations. /// Provides functionality for PUBLISH, SUBSCRIBE, and UNSUBSCRIBE commands. /// [Verb("publish", HelpText = "Publish a message to a channel.")] public class PublishOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Value(0, MetaName = "channel", Required = true, HelpText = "Channel name.")] public string Channel { get; set; } [Value(1, MetaName = "message", Required = true, HelpText = "Message to publish.")] public string Message { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } [Verb("subscribe", HelpText = "Subscribe to one or more channels.")] public class SubscribeOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Value(0, MetaName = "channels", Min = 1, Required = true, HelpText = "Channels to subscribe to.")] public IEnumerable Channels { get; set; } } [Verb("unsubscribe", HelpText = "Unsubscribe from one or more channels.")] public class UnsubscribeOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Value(0, MetaName = "channels", Min = 1, Required = true, HelpText = "Channels to unsubscribe from.")] public IEnumerable Channels { get; set; } } public static class PubSubCommands { /// /// Executes the PUBLISH command to publish a message to a channel. /// /// The PublishOptions containing instance, channel, and message. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunPublish(PublishOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var sub = redis.GetSubscriber(); var count = sub.Publish(opts.Channel, opts.Message); if (opts.Table) RedisUtils.PrintTable(new[] { "Channel", "Receivers" }, new List { new[] { opts.Channel, count.ToString() } }); else Console.WriteLine(Output.Green($"Published to {opts.Channel}, receivers: {count}")); return 0; } /// /// Executes the SUBSCRIBE command to subscribe to one or more channels. /// /// The SubscribeOptions containing instance and channels. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunSubscribe(SubscribeOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var sub = redis.GetSubscriber(); var channels = opts.Channels; Console.WriteLine(Output.Yellow($"Subscribed to: {string.Join(", ", channels)}. Press Ctrl+C to exit.")); using var cts = new CancellationTokenSource(); Console.CancelKeyPress += (s, e) => { e.Cancel = true; cts.Cancel(); }; foreach (var channel in channels) { sub.Subscribe(channel, (ch, msg) => { Console.WriteLine(Output.Cyan($"[{ch}] {msg}")); }); } try { while (!cts.IsCancellationRequested) { Thread.Sleep(100); } } catch (OperationCanceledException) { } Console.WriteLine(Output.Yellow("Unsubscribed and exiting.")); return 0; } /// /// Executes the UNSUBSCRIBE command to unsubscribe from one or more channels. /// /// The UnsubscribeOptions containing instance and channels. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunUnsubscribe(UnsubscribeOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var sub = redis.GetSubscriber(); foreach (var channel in opts.Channels) { sub.Unsubscribe(channel); Console.WriteLine(Output.Yellow($"Unsubscribed from {channel}")); } return 0; } } }