mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Merge branch 'user-pfp-discord-rpc' of https://github.com/axellse/bloxstrap into user-pfp-discord-rpc
This commit is contained in:
commit
188242f479
@ -110,22 +110,24 @@ namespace Bloxstrap
|
||||
public static async Task<GithubRelease?> GetLatestRelease()
|
||||
{
|
||||
const string LOG_IDENT = "App::GetLatestRelease";
|
||||
|
||||
GithubRelease? releaseInfo = null;
|
||||
|
||||
try
|
||||
{
|
||||
releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");
|
||||
var releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");
|
||||
|
||||
if (releaseInfo is null || releaseInfo.Assets is null)
|
||||
{
|
||||
Logger.WriteLine(LOG_IDENT, "Encountered invalid data");
|
||||
return null;
|
||||
}
|
||||
|
||||
return releaseInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.WriteException(LOG_IDENT, ex);
|
||||
}
|
||||
|
||||
return releaseInfo;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
|
13
Bloxstrap/Exceptions/InvalidHTTPResponseException.cs
Normal file
13
Bloxstrap/Exceptions/InvalidHTTPResponseException.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bloxstrap.Exceptions
|
||||
{
|
||||
internal class InvalidHTTPResponseException : Exception
|
||||
{
|
||||
public InvalidHTTPResponseException(string message) : base(message) { }
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace Bloxstrap.Integrations
|
||||
using System.Windows;
|
||||
|
||||
namespace Bloxstrap.Integrations
|
||||
{
|
||||
public class ActivityWatcher : IDisposable
|
||||
{
|
||||
@ -388,21 +390,19 @@
|
||||
string location = "";
|
||||
var ipInfo = await Http.GetJson<IPInfoResponse>($"https://ipinfo.io/{Data.MachineAddress}/json");
|
||||
|
||||
if (ipInfo is null)
|
||||
return $"? ({Strings.ActivityTracker_LookupFailed})";
|
||||
if (String.IsNullOrEmpty(ipInfo.City))
|
||||
throw new InvalidHTTPResponseException("Reported city was blank");
|
||||
|
||||
if (string.IsNullOrEmpty(ipInfo.Country))
|
||||
location = "?";
|
||||
else if (ipInfo.City == ipInfo.Region)
|
||||
if (ipInfo.City == ipInfo.Region)
|
||||
location = $"{ipInfo.Region}, {ipInfo.Country}";
|
||||
else
|
||||
location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}";
|
||||
|
||||
if (!InGame)
|
||||
return $"? ({Strings.ActivityTracker_LeftGame})";
|
||||
|
||||
GeolocationCache[Data.MachineAddress] = location;
|
||||
|
||||
if (!InGame)
|
||||
return "";
|
||||
|
||||
return location;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -410,7 +410,9 @@
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {Data.MachineAddress}");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
return $"? ({Strings.ActivityTracker_LookupFailed})";
|
||||
Frontend.ShowMessageBox($"{Strings.ActivityWatcher_LocationQueryFailed}\n\n{ex.Message}", MessageBoxImage.Warning);
|
||||
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System.Windows;
|
||||
using DiscordRPC;
|
||||
|
||||
namespace Bloxstrap.Integrations
|
||||
@ -206,18 +207,22 @@ namespace Bloxstrap.Integrations
|
||||
timeStarted = activity.RootActivity.TimeJoined;
|
||||
|
||||
if (activity.UniverseDetails is null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UniverseDetails.FetchSingle(activity.UniverseId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
Frontend.ShowMessageBox($"{Strings.ActivityWatcher_RichPresenceLoadFailed}\n\n{ex.Message}", MessageBoxImage.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.UniverseDetails = UniverseDetails.LoadFromCache(activity.UniverseId);
|
||||
}
|
||||
|
||||
var universeDetails = activity.UniverseDetails;
|
||||
|
||||
if (universeDetails is null)
|
||||
{
|
||||
Frontend.ShowMessageBox(Strings.ActivityTracker_RichPresenceLoadFailed, System.Windows.MessageBoxImage.Warning);
|
||||
return false;
|
||||
}
|
||||
var universeDetails = activity.UniverseDetails!;
|
||||
|
||||
icon = universeDetails.Thumbnail.ImageUrl;
|
||||
|
||||
|
@ -46,7 +46,7 @@ namespace Bloxstrap
|
||||
message = Strings.JsonManager_FastFlagsLoadFailed;
|
||||
|
||||
if (!String.IsNullOrEmpty(message))
|
||||
Frontend.ShowMessageBox($"{message}\n\n{ex.GetType()}: {ex.Message}", System.Windows.MessageBoxImage.Warning);
|
||||
Frontend.ShowMessageBox($"{message}\n\n{ex.Message}", System.Windows.MessageBoxImage.Warning);
|
||||
}
|
||||
|
||||
Save();
|
||||
|
@ -21,17 +21,19 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Task<bool> FetchSingle(long id) => FetchBulk(id.ToString());
|
||||
public static Task FetchSingle(long id) => FetchBulk(id.ToString());
|
||||
|
||||
public static async Task<bool> FetchBulk(string ids)
|
||||
public static async Task FetchBulk(string ids)
|
||||
{
|
||||
var gameDetailResponse = await Http.GetJson<ApiArrayResponse<GameDetailResponse>>($"https://games.roblox.com/v1/games?universeIds={ids}");
|
||||
if (gameDetailResponse is null || !gameDetailResponse.Data.Any())
|
||||
return false;
|
||||
|
||||
if (!gameDetailResponse.Data.Any())
|
||||
throw new InvalidHTTPResponseException("Roblox API for Game Details returned invalid data");
|
||||
|
||||
var universeThumbnailResponse = await Http.GetJson<ApiArrayResponse<ThumbnailResponse>>($"https://thumbnails.roblox.com/v1/games/icons?universeIds={ids}&returnPolicy=PlaceHolder&size=128x128&format=Png&isCircular=false");
|
||||
if (universeThumbnailResponse is null || !universeThumbnailResponse.Data.Any())
|
||||
return false;
|
||||
|
||||
if (!universeThumbnailResponse.Data.Any())
|
||||
throw new InvalidHTTPResponseException("Roblox API for Game Thumbnails returned invalid data");
|
||||
|
||||
foreach (string strId in ids.Split(','))
|
||||
{
|
||||
@ -43,8 +45,6 @@
|
||||
Thumbnail = universeThumbnailResponse.Data.Where(x => x.TargetId == id).First(),
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
19
Bloxstrap/Resources/Strings.Designer.cs
generated
19
Bloxstrap/Resources/Strings.Designer.cs
generated
@ -106,29 +106,20 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to left game.
|
||||
/// Looks up a localized string similar to Failed to query server location..
|
||||
/// </summary>
|
||||
public static string ActivityTracker_LeftGame {
|
||||
public static string ActivityWatcher_LocationQueryFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTracker.LeftGame", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to lookup failed.
|
||||
/// </summary>
|
||||
public static string ActivityTracker_LookupFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTracker.LookupFailed", resourceCulture);
|
||||
return ResourceManager.GetString("ActivityWatcher.LocationQueryFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your current game will not show on your Discord presence because an error occurred when loading the game information..
|
||||
/// </summary>
|
||||
public static string ActivityTracker_RichPresenceLoadFailed {
|
||||
public static string ActivityWatcher_RichPresenceLoadFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTracker.RichPresenceLoadFailed", resourceCulture);
|
||||
return ResourceManager.GetString("ActivityWatcher.RichPresenceLoadFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,12 +117,6 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ActivityTracker.LeftGame" xml:space="preserve">
|
||||
<value>left game</value>
|
||||
</data>
|
||||
<data name="ActivityTracker.LookupFailed" xml:space="preserve">
|
||||
<value>lookup failed</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.AutoUpdateFailed" xml:space="preserve">
|
||||
<value>Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website.</value>
|
||||
</data>
|
||||
@ -1177,10 +1171,13 @@ Are you sure you want to continue?</value>
|
||||
<data name="ContextMenu.GameHistory.Rejoin" xml:space="preserve">
|
||||
<value>Rejoin</value>
|
||||
</data>
|
||||
<data name="ActivityTracker.RichPresenceLoadFailed" xml:space="preserve">
|
||||
<data name="ActivityWatcher.RichPresenceLoadFailed" xml:space="preserve">
|
||||
<value>Your current game will not show on your Discord presence because an error occurred when loading the game information.</value>
|
||||
</data>
|
||||
<data name="ContextMenu.GameHistory.Description" xml:space="preserve">
|
||||
<value>Game history is only recorded for your current Roblox session. Games will appear here as you leave them or teleport within them.</value>
|
||||
</data>
|
||||
<data name="ActivityWatcher.LocationQueryFailed" xml:space="preserve">
|
||||
<value>Failed to query server location.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -8,6 +8,7 @@
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels.ContextMenu"
|
||||
xmlns:resources="clr-namespace:Bloxstrap.Resources"
|
||||
xmlns:enums="clr-namespace:Bloxstrap.Enums"
|
||||
d:DataContext="{d:DesignInstance Type=models:ServerHistoryViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{x:Static resources:Strings.ContextMenu_GameHistory_Title}"
|
||||
@ -30,11 +31,24 @@
|
||||
|
||||
<TextBlock Grid.Row="1" Margin="16,8,16,0" Text="{x:Static resources:Strings.ContextMenu_GameHistory_Description}" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Grid.Row="2" Margin="16,8,16,0" Text="{Binding Error, Mode=OneWay}" TextWrapping="Wrap">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Failed}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<Border Grid.Row="2">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding GameHistory, Mode=OneWay}" Value="{x:Null}">
|
||||
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Unknown}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
@ -49,11 +63,11 @@
|
||||
<ListView.Style>
|
||||
<Style TargetType="ListView" BasedOn="{StaticResource {x:Type ListView}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding GameHistory, Mode=OneWay}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Successful}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</ListView.Style>
|
||||
<ListView.ItemTemplate>
|
||||
|
@ -60,6 +60,10 @@ namespace Bloxstrap.UI
|
||||
return;
|
||||
|
||||
string serverLocation = await _activityWatcher.GetServerLocation();
|
||||
|
||||
if (string.IsNullOrEmpty(serverLocation))
|
||||
return;
|
||||
|
||||
string title = _activityWatcher.Data.ServerType switch
|
||||
{
|
||||
ServerType.Public => Strings.ContextMenu_ServerInformation_Notification_Title_Public,
|
||||
|
@ -10,6 +10,10 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu
|
||||
|
||||
public List<ActivityData>? GameHistory { get; private set; }
|
||||
|
||||
public GenericTriState LoadState { get; private set; } = GenericTriState.Unknown;
|
||||
|
||||
public string Error { get; private set; } = String.Empty;
|
||||
|
||||
public ICommand CloseWindowCommand => new RelayCommand(RequestClose);
|
||||
|
||||
public EventHandler? RequestCloseEvent;
|
||||
@ -25,6 +29,9 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu
|
||||
|
||||
private async void LoadData()
|
||||
{
|
||||
LoadState = GenericTriState.Unknown;
|
||||
OnPropertyChanged(nameof(LoadState));
|
||||
|
||||
var entries = _activityWatcher.History.Where(x => x.UniverseDetails is null);
|
||||
|
||||
if (entries.Any())
|
||||
@ -32,8 +39,22 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu
|
||||
// TODO: this will duplicate universe ids
|
||||
string universeIds = String.Join(',', entries.Select(x => x.UniverseId));
|
||||
|
||||
if (!await UniverseDetails.FetchBulk(universeIds))
|
||||
try
|
||||
{
|
||||
await UniverseDetails.FetchBulk(universeIds);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteException("ServerHistoryViewModel::LoadData", ex);
|
||||
|
||||
Error = ex.Message;
|
||||
OnPropertyChanged(nameof(Error));
|
||||
|
||||
LoadState = GenericTriState.Failed;
|
||||
OnPropertyChanged(nameof(LoadState));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var entry in entries)
|
||||
entry.UniverseDetails = UniverseDetails.LoadFromCache(entry.UniverseId);
|
||||
@ -64,6 +85,9 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(GameHistory));
|
||||
|
||||
LoadState = GenericTriState.Successful;
|
||||
OnPropertyChanged(nameof(LoadState));
|
||||
}
|
||||
|
||||
private void RequestClose() => RequestCloseEvent?.Invoke(this, EventArgs.Empty);
|
||||
|
@ -5,14 +5,6 @@ namespace Bloxstrap
|
||||
{
|
||||
static class Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Is process running as administrator
|
||||
/// https://stackoverflow.com/a/11660205
|
||||
/// </summary>
|
||||
public static bool IsAdministrator =>
|
||||
new WindowsPrincipal(WindowsIdentity.GetCurrent())
|
||||
.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
public static void ShellExecute(string website)
|
||||
{
|
||||
try
|
||||
|
@ -2,22 +2,22 @@
|
||||
{
|
||||
internal static class Http
|
||||
{
|
||||
public static async Task<T?> GetJson<T>(string url)
|
||||
/// <summary>
|
||||
/// Gets and deserializes a JSON API response to the specified object
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="url"></param>
|
||||
/// <exception cref="HttpRequestException"></exception>
|
||||
/// <exception cref="JsonException"></exception>
|
||||
public static async Task<T> GetJson<T>(string url)
|
||||
{
|
||||
string LOG_IDENT = $"Http::GetJson<{typeof(T).Name}>";
|
||||
var request = await App.HttpClient.GetAsync(url);
|
||||
|
||||
string json = await App.HttpClient.GetStringAsync(url);
|
||||
request.EnsureSuccessStatusCode();
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to deserialize JSON for {url}!");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
return default;
|
||||
}
|
||||
string json = await request.Content.ReadAsStringAsync();
|
||||
|
||||
return JsonSerializer.Deserialize<T>(json)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user