using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DiscordRPC; using Bloxstrap.Models.RobloxApi; using Bloxstrap.Models; namespace Bloxstrap.Integrations { public class DiscordRichPresence : IDisposable { private readonly DiscordRpcClient _rpcClient = new("1005469189907173486"); private readonly RobloxActivity _activityWatcher; private RichPresence? _currentPresence; private string? _initialStatus; private long _currentUniverseId; private DateTime? _timeStartedUniverse; public DiscordRichPresence(RobloxActivity activityWatcher) { _activityWatcher = activityWatcher; _activityWatcher.OnGameJoin += (_, _) => Task.Run(() => SetCurrentGame()); _activityWatcher.OnGameLeave += (_, _) => Task.Run(() => SetCurrentGame()); _activityWatcher.OnGameMessage += (_, message) => OnGameMessage(message); _rpcClient.OnReady += (_, e) => App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Received ready from user {e.User.Username} ({e.User.ID})"); _rpcClient.OnPresenceUpdate += (_, e) => App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Updated presence"); _rpcClient.OnError += (_, e) => App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] An RPC error occurred - {e.Message}"); _rpcClient.OnConnectionEstablished += (_, e) => App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Established connection with Discord RPC"); //spams log as it tries to connect every ~15 sec when discord is closed so not now //_rpcClient.OnConnectionFailed += (_, e) => // App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Failed to establish connection with Discord RPC"); _rpcClient.OnClose += (_, e) => App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Lost connection to Discord RPC - {e.Reason} ({e.Code})"); _rpcClient.Initialize(); } public void OnGameMessage(GameMessage message) { if (message.Command == "SetPresenceStatus") SetStatus(message.Data); } public void SetStatus(string status) { App.Logger.WriteLine($"[DiscordRichPresence::SetStatus] Setting status to '{status}'"); if (_currentPresence is null) { App.Logger.WriteLine($"[DiscordRichPresence::SetStatus] Presence is not set, aborting"); return; } if (status.Length > 128) { App.Logger.WriteLine($"[DiscordRichPresence::SetStatus] Status cannot be longer than 128 characters, aborting"); return; } if (_initialStatus is null) _initialStatus = _currentPresence.State; string finalStatus; if (string.IsNullOrEmpty(status)) { App.Logger.WriteLine($"[DiscordRichPresence::SetStatus] Status is empty, reverting to initial status"); finalStatus = _initialStatus; } else { finalStatus = status; } if (_currentPresence.State == finalStatus) { App.Logger.WriteLine($"[DiscordRichPresence::SetStatus] Status is unchanged, aborting"); return; } _currentPresence.State = finalStatus; UpdatePresence(); } public async Task SetCurrentGame() { if (!_activityWatcher.ActivityInGame) { App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Not in game, clearing presence"); _currentPresence = null; _initialStatus = null; UpdatePresence(); return true; } string icon = "roblox"; App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Setting presence for Place ID {_activityWatcher.ActivityPlaceId}"); var universeIdResponse = await Utilities.GetJson($"https://apis.roblox.com/universes/v1/places/{_activityWatcher.ActivityPlaceId}/universe"); if (universeIdResponse is null) { App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Could not get Universe ID!"); return false; } long universeId = universeIdResponse.UniverseId; App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Got Universe ID as {universeId}"); // preserve time spent playing if we're teleporting between places in the same universe if (_timeStartedUniverse is null || !_activityWatcher.ActivityIsTeleport || universeId != _currentUniverseId) _timeStartedUniverse = DateTime.UtcNow; _activityWatcher.ActivityIsTeleport = false; _currentUniverseId = universeId; var gameDetailResponse = await Utilities.GetJson>($"https://games.roblox.com/v1/games?universeIds={universeId}"); if (gameDetailResponse is null || !gameDetailResponse.Data.Any()) { App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Could not get Universe info!"); return false; } GameDetailResponse universeDetails = gameDetailResponse.Data.ToArray()[0]; App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Got Universe details"); var universeThumbnailResponse = await Utilities.GetJson>($"https://thumbnails.roblox.com/v1/games/icons?universeIds={universeId}&returnPolicy=PlaceHolder&size=512x512&format=Png&isCircular=false"); if (universeThumbnailResponse is null || !universeThumbnailResponse.Data.Any()) { App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Could not get Universe thumbnail info!"); } else { icon = universeThumbnailResponse.Data.ToArray()[0].ImageUrl; App.Logger.WriteLine($"[DiscordRichPresence::SetCurrentGame] Got Universe thumbnail as {icon}"); } List