From 55f5ef48e853ed68ae361dde1c06f3dad42644dd Mon Sep 17 00:00:00 2001 From: axel Date: Sat, 28 Sep 2024 22:52:12 +0200 Subject: [PATCH] Show account on discord rich presence (#2255) * add functionality for grabbing userid * Create UserInfoResponse.cs * add user pfp small image thingy * add missing semicolons * get rid of semicolons * debugging (remove later) * make properties nullable * forgot to put dollar sign before interpolated string * make properties that cant be nullable not nullable * more debugging * remove thing * remove other thing * remove thing (again) (again) * remove thing (again) (again) (again) * add space between username and displayname to make it more visually pleasing * matt review changes (better code readability) * add strings for show account on profile * add AccountShownOnProfile setting * add DiscordActivityJoinEnabled to integrations viewmodel * fix accidentally swapping 2 variables * refrence correct variables * refrence correct variables (again) * add showaccountonprofile strings * add option to integrations page * add missing < that somehow got lost * make that its own option * dont invert that value * dont invert that (again) * Update IntegrationsViewModel.cs * fix grammatical issue in string * move else to new line * fix merge conflicts * move gameJoinLoadTime check * matt review changes * handle if parsing userid fails --------- Co-authored-by: pizzaboxer --- Bloxstrap/Integrations/ActivityWatcher.cs | 27 ++++++++++++- Bloxstrap/Integrations/DiscordRichPresence.cs | 38 ++++++++++++++++--- .../Models/APIs/Roblox/UserInfoResponse.cs | 26 +++++++++++++ Bloxstrap/Models/Entities/ActivityData.cs | 2 + Bloxstrap/Models/Persistable/Settings.cs | 1 + Bloxstrap/Resources/Strings.Designer.cs | 18 +++++++++ Bloxstrap/Resources/Strings.resx | 6 +++ .../Settings/Pages/IntegrationsPage.xaml | 7 ++++ .../Settings/IntegrationsViewModel.cs | 8 ++++ 9 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 Bloxstrap/Models/APIs/Roblox/UserInfoResponse.cs diff --git a/Bloxstrap/Integrations/ActivityWatcher.cs b/Bloxstrap/Integrations/ActivityWatcher.cs index b1d13d4f..73b3b80c 100644 --- a/Bloxstrap/Integrations/ActivityWatcher.cs +++ b/Bloxstrap/Integrations/ActivityWatcher.cs @@ -17,7 +17,9 @@ public class ActivityWatcher : IDisposable private const string GameDisconnectedEntry = "[FLog::Network] Time to disconnect replication data:"; private const string GameTeleportingEntry = "[FLog::SingleSurfaceApp] initiateTeleport"; private const string GameLeavingEntry = "[FLog::SingleSurfaceApp] leaveUGCGameInternal"; + private const string GameJoinLoadTimeEntry = "[FLog::GameJoinLoadTime] Report game_join_loadtime:"; + private const string GameJoinLoadTimeEntryPattern = ", userid:([0-9]+)"; private const string GameJoiningEntryPattern = @"! Joining game '([0-9a-f\-]{36})' place ([0-9]+) at ([0-9\.]+)"; private const string GameJoiningPrivateServerPattern = @"""accessCode"":""([0-9a-f\-]{36})"""; private const string GameJoiningUniversePattern = @"universeid:([0-9]+)"; @@ -153,7 +155,7 @@ private void ReadLogEntry(string entry) if (!InGame && Data.PlaceId == 0) { // We are not in a game, nor are in the process of joining one - + if (entry.Contains(GameJoiningPrivateServerEntry)) { // we only expect to be joining a private server if we're not already in a game @@ -209,6 +211,28 @@ private void ReadLogEntry(string entry) { // We are not confirmed to be in a game, but we are in the process of joining one + if (entry.Contains(GameJoinLoadTimeEntry)) + { + Match match = Regex.Match(entry, GameJoinLoadTimeEntryPattern); + + if (match.Groups.Count != 2) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to assert format for game join load time entry"); + App.Logger.WriteLine(LOG_IDENT, entry); + return; + } + + if (!UInt64.TryParse(match.Groups[1].Value, out ulong result)) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to parse userid from game join load time entry"); + App.Logger.WriteLine(LOG_IDENT, match.Groups[1].Value); + return; + } + + Data.UserId = result; + App.Logger.WriteLine(LOG_IDENT, $"Got userid as {Data.UserId}"); + } + if (entry.Contains(GameJoiningUniverseEntry)) { var match = Regex.Match(entry, GameJoiningUniversePattern); @@ -279,7 +303,6 @@ private void ReadLogEntry(string entry) History.Insert(0, Data); InGame = false; - Data = new(); OnGameLeave?.Invoke(this, new EventArgs()); diff --git a/Bloxstrap/Integrations/DiscordRichPresence.cs b/Bloxstrap/Integrations/DiscordRichPresence.cs index ba1f719c..38619184 100644 --- a/Bloxstrap/Integrations/DiscordRichPresence.cs +++ b/Bloxstrap/Integrations/DiscordRichPresence.cs @@ -1,5 +1,5 @@ -using System.Windows; - +using System.Windows; +using Bloxstrap.Models.RobloxApi; using DiscordRPC; namespace Bloxstrap.Integrations @@ -192,6 +192,9 @@ public async Task SetCurrentGame() } string icon = "roblox"; + string smallImageText = "Roblox"; + string smallImage = "roblox"; + var activity = _activityWatcher.Data; long placeId = activity.PlaceId; @@ -224,6 +227,31 @@ public async Task SetCurrentGame() icon = universeDetails.Thumbnail.ImageUrl; + if (App.Settings.Prop.AccountShownOnProfile) + { + var userPfpResponse = await Http.GetJson>($"https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds={activity.UserId}&size=180x180&format=Png&isCircular=false"); //we can remove '-headshot' from the url if we want a full avatar picture + if (userPfpResponse is null || !userPfpResponse.Data.Any()) + { + App.Logger.WriteLine(LOG_IDENT, "Could not get user thumbnail info!"); + } + else + { + smallImage = userPfpResponse.Data.First().ImageUrl; + App.Logger.WriteLine(LOG_IDENT, $"Got user thumbnail as {smallImage}"); + } + + var userInfoResponse = await Http.GetJson($"https://users.roblox.com/v1/users/{activity.UserId}"); + if (userInfoResponse is null) + { + App.Logger.WriteLine(LOG_IDENT, "Could not get user info!"); + } + else + { + smallImageText = userInfoResponse.DisplayName + $" (@{userInfoResponse.Username})"; //example: john doe (@johndoe) + App.Logger.WriteLine(LOG_IDENT, $"Got user info as {smallImageText}"); + } + } + if (!_activityWatcher.InGame || placeId != activity.PlaceId) { App.Logger.WriteLine(LOG_IDENT, "Aborting presence set because game activity has changed"); @@ -251,9 +279,9 @@ public async Task SetCurrentGame() Assets = new Assets { LargeImageKey = icon, - LargeImageText = universeName, - SmallImageKey = "roblox", - SmallImageText = "Roblox" + LargeImageText = universeDetails.Data.Name, + SmallImageKey = smallImage, + SmallImageText = smallImageText } }; diff --git a/Bloxstrap/Models/APIs/Roblox/UserInfoResponse.cs b/Bloxstrap/Models/APIs/Roblox/UserInfoResponse.cs new file mode 100644 index 00000000..c043a8f7 --- /dev/null +++ b/Bloxstrap/Models/APIs/Roblox/UserInfoResponse.cs @@ -0,0 +1,26 @@ +namespace Bloxstrap.Models.RobloxApi +{ + /// + /// Roblox.Web.Responses.Users.UserInfoResponse + /// + public class UserInfoResponse + { + [JsonPropertyName("description")] + public string ProfileDescription { get; set; } = null!; + + [JsonPropertyName("created")] + public string JoinDate { get; set; } = null!; + + [JsonPropertyName("isBanned")] + public bool IsBanned { get; set; } + + [JsonPropertyName("hasVerifiedBadge")] + public bool HasVerifiedBadge { get; set; } + + [JsonPropertyName("name")] + public string Username { get; set; } = null!; + + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = null!; + } +} diff --git a/Bloxstrap/Models/Entities/ActivityData.cs b/Bloxstrap/Models/Entities/ActivityData.cs index 40565f07..8cebff2a 100644 --- a/Bloxstrap/Models/Entities/ActivityData.cs +++ b/Bloxstrap/Models/Entities/ActivityData.cs @@ -34,6 +34,8 @@ public long UniverseId /// This will be empty unless the server joined is a private server /// public string AccessCode { get; set; } = string.Empty; + + public ulong UserId { get; set; } = 0; public string MachineAddress { get; set; } = string.Empty; diff --git a/Bloxstrap/Models/Persistable/Settings.cs b/Bloxstrap/Models/Persistable/Settings.cs index bbfc2850..3b753f0f 100644 --- a/Bloxstrap/Models/Persistable/Settings.cs +++ b/Bloxstrap/Models/Persistable/Settings.cs @@ -21,6 +21,7 @@ public class Settings public bool EnableActivityTracking { get; set; } = true; public bool UseDiscordRichPresence { get; set; } = true; public bool HideRPCButtons { get; set; } = true; + public bool AccountShownOnProfile { get; set; } = true; public bool ShowServerDetails { get; set; } = false; public ObservableCollection CustomIntegrations { get; set; } = new(); diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index c0f1bce4..f8464a80 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -2664,6 +2664,24 @@ public static string Menu_Integrations_AllowActivityJoining_Description { return ResourceManager.GetString("Menu.Integrations.AllowActivityJoining.Description", resourceCulture); } } + + /// + /// Looks up a localized string similar to Show account on profile. + /// + public static string Menu_Integrations_ShowAccountOnProfile_Title { + get { + return ResourceManager.GetString("Menu.Integrations.ShowAccountOnProfile.Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shows what Roblox account your using on your Discord profile. + /// + public static string Menu_Integrations_ShowAccountOnProfile_Description { + get { + return ResourceManager.GetString("Menu.Integrations.ShowAccountOnProfile.Description", resourceCulture); + } + } /// /// Looks up a localized string similar to Allow activity joining. diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 76d30117..dc3cd26f 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -701,6 +701,12 @@ Selecting 'No' will ignore this warning and continue installation. Allow activity joining + + Shows which Roblox account you are using on your Discord profile. + + + Show account on profile + Application Location diff --git a/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml b/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml index 9f409a9d..8be8d717 100644 --- a/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml +++ b/Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml @@ -58,6 +58,13 @@ + + + + diff --git a/Bloxstrap/UI/ViewModels/Settings/IntegrationsViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/IntegrationsViewModel.cs index 042b1382..78b007c8 100644 --- a/Bloxstrap/UI/ViewModels/Settings/IntegrationsViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Settings/IntegrationsViewModel.cs @@ -100,7 +100,9 @@ public bool DiscordActivityEnabled if (!value) { DiscordActivityJoinEnabled = value; + DiscordAccountOnProfile = value; OnPropertyChanged(nameof(DiscordActivityJoinEnabled)); + OnPropertyChanged(nameof(DiscordAccountOnProfile)); } } } @@ -111,6 +113,12 @@ public bool DiscordActivityJoinEnabled set => App.Settings.Prop.HideRPCButtons = !value; } + public bool DiscordAccountOnProfile + { + get => App.Settings.Prop.AccountShownOnProfile; + set => App.Settings.Prop.AccountShownOnProfile = value; + } + public bool DisableAppPatchEnabled { get => App.Settings.Prop.UseDisableAppPatch;