using System; using DiscordRPC; using DiscordRPC.Logging; using DiscordRPC.Message; using Microsoft.Extensions.Logging; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Services; namespace StabilityMatrix.Avalonia.Services; public class DiscordRichPresenceService : IDiscordRichPresenceService { private const string ApplicationId = "1134669805237059615"; private readonly ILogger logger; private readonly ISettingsManager settingsManager; private readonly DiscordRpcClient client; private readonly string appDetails; private bool isDisposed; private RichPresence DefaultPresence => new() { Details = appDetails, Assets = new DiscordRPC.Assets { LargeImageKey = "stabilitymatrix-logo-1", LargeImageText = $"Stability Matrix {appDetails}", }, Buttons = new[] { new Button { Label = "GitHub", Url = "https://github.com/LykosAI/StabilityMatrix", } } }; public DiscordRichPresenceService( ILogger logger, ISettingsManager settingsManager) { this.logger = logger; this.settingsManager = settingsManager; appDetails = $"v{Compat.AppVersion.WithoutMetadata()}"; client = new DiscordRpcClient(ApplicationId); client.Logger = new NullLogger(); client.OnReady += OnReady; client.OnError += OnError; client.OnClose += OnClose; client.OnPresenceUpdate += OnPresenceUpdate; settingsManager.SettingsPropertyChanged += (sender, args) => { if (args.PropertyName == nameof(settingsManager.Settings.IsDiscordRichPresenceEnabled)) { UpdateState(); } }; EventManager.Instance.RunningPackageStatusChanged += OnRunningPackageStatusChanged; } private void OnReady(object sender, ReadyMessage args) { logger.LogInformation("Received Ready from user {User}", args.User.Username); } private void OnError(object sender, ErrorMessage args) { logger.LogWarning("Received Error: {Message}", args.Message); } private void OnClose(object sender, CloseMessage args) { logger.LogInformation("Received Close: {Reason}", args.Reason); } private void OnPresenceUpdate(object sender, PresenceMessage args) { logger.LogDebug("Received Update: {Presence}", args.Presence.ToString()); } private void OnRunningPackageStatusChanged(object? sender, RunningPackageStatusChangedEventArgs args) { if (!client.IsInitialized || !settingsManager.Settings.IsDiscordRichPresenceEnabled) return; if (args.CurrentPackagePair is null) { client.SetPresence(DefaultPresence); } else { var presence = DefaultPresence; var packageTitle = args.CurrentPackagePair.BasePackage switch { A3WebUI => "Automatic1111 Web UI", VladAutomatic => "SD.Next Web UI", ComfyUI => "ComfyUI", VoltaML => "VoltaML", InvokeAI => "InvokeAI", _ => "Stable Diffusion" }; presence.State = $"Running {packageTitle}"; presence.Assets.SmallImageText = presence.State; presence.Assets.SmallImageKey = args.CurrentPackagePair.BasePackage switch { ComfyUI => "fa_diagram_project", VoltaML => "ic_package_voltaml", InvokeAI => "ic_package_invokeai", _ => "ic_fluent_box_512_filled" }; presence.WithTimestamps(new Timestamps { StartUnixMilliseconds = (ulong?) DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }); client.SetPresence(presence); } } public void UpdateState() { // Set initial rich presence if (settingsManager.Settings.IsDiscordRichPresenceEnabled) { lock (client) { if (!client.IsInitialized) { client.Initialize(); client.SetPresence(DefaultPresence); } } } else { lock (client) { if (client.IsInitialized) { client.ClearPresence(); client.Deinitialize(); } } } } public void Dispose() { if (!isDisposed) { if (client.IsInitialized) { client.ClearPresence(); } client.Dispose(); EventManager.Instance.RunningPackageStatusChanged -= OnRunningPackageStatusChanged; } isDisposed = true; GC.SuppressFinalize(this); } ~DiscordRichPresenceService() { Dispose(); } }