diff --git a/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs b/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs index 456d2761..b79e03ef 100644 --- a/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs +++ b/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs @@ -50,7 +50,9 @@ namespace CompatApiClient.Compression } } request.Headers?.Remove(PostCompressionFlag); + //ApiConfig.Log.Trace($"{request.Method} {request.RequestUri}"); var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + //ApiConfig.Log.Trace($"Response: {response.StatusCode} {request.RequestUri}"); if (isClient && response.Content?.Headers?.ContentEncoding != null) { var encoding = response.Content.Headers.ContentEncoding.FirstOrDefault(); diff --git a/Clients/PsnClient/POCOs/Container.cs b/Clients/PsnClient/POCOs/Container.cs index 78c7fc19..0a28add1 100644 --- a/Clients/PsnClient/POCOs/Container.cs +++ b/Clients/PsnClient/POCOs/Container.cs @@ -192,4 +192,11 @@ namespace PsnClient.POCOs public string Id; public string Name; } + + public class FirmwareInfo + { + public string Version; + public string DownloadUrl; + public string Locale; + } } diff --git a/Clients/PsnClient/PsnClient.cs b/Clients/PsnClient/PsnClient.cs index cf73d570..c56eb27b 100644 --- a/Clients/PsnClient/PsnClient.cs +++ b/Clients/PsnClient/PsnClient.cs @@ -33,6 +33,11 @@ namespace PsnClient "en-SK", "en-TH", "en-TR", "en-TW", "en-ZA", "ja-JP", "de-AT", "de-CH", "de-DE", "de-LU", "es-BO", "es-CR", "es-EC", "es-ES", "es-GT", "es-HN", "es-NI", "es-PA", "es-PY", "es-SV", "es-UY", "fr-BE", "fr-FR", "it-IT", "ko-KR", "nl-NL", "pt-PT", "ru-RU", "ru-UA", "zh-Hans-CN" }; + // Dest=87;ImageVersion=0001091d;SystemSoftwareVersion=4.8500;CDN=http://duk01.ps3.update.playstation.net/update/ps3/image/uk/2019_0828_c975768e5d70e105a72656f498cc9be9/PS3UPDAT.PUP;CDN_Timeout=30; + private static readonly Regex FwVersionInfo = new Regex(@"Dest=(?\d+);ImageVersion=(?[0-9a-f]+);SystemSoftwareVersion=(?\d+\.\d+);CDN=(?http[^;]+);CDN_Timeout=(?\d+)", + RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnoreCase); + + private static readonly string[] KnownFwLocales = { "us", "eu", "uk", "au", "ru", "jp", "br", "cn", "hk", "mx", "sa", "tw", "kr", }; public Client() { @@ -321,6 +326,34 @@ namespace PsnClient } } + public async Task> GetHighestFwVersionAsync(CancellationToken cancellationToken) + { + var tasks = new List>(KnownFwLocales.Length); + foreach (var fwLocale in KnownFwLocales) + tasks.Add(GetFwVersionAsync(fwLocale, cancellationToken)); + var allVersions = new List(KnownFwLocales.Length); + foreach (var t in tasks) + try + { + var ver = await t.ConfigureAwait(false); + if (ver == null) + continue; + + allVersions.Add(ver); + } + catch { } + + allVersions = allVersions.OrderByDescending(fwi => fwi.Version).ToList(); + if (allVersions.Any()) + { + var maxFw = allVersions.First(); + var result = allVersions.Where(fwi => fwi.Version == maxFw.Version).ToList(); + return result; + } + + return new List(0); + } + private async Task GetSessionCookies(string locale, CancellationToken cancellationToken) { var loc = locale.AsLocaleData(); @@ -370,5 +403,50 @@ namespace PsnClient } while (tries < 3); throw new InvalidOperationException("Couldn't obtain web session"); } + + private async Task GetFwVersionAsync(string fwLocale, CancellationToken cancellationToken) + { + var uri = new Uri($"http://f{fwLocale}01.ps3.update.playstation.net/update/ps3/list/{fwLocale}/ps3-updatelist.txt"); + try + { + using (var message = new HttpRequestMessage(HttpMethod.Get, uri)) + using (var response = await client.SendAsync(message, cancellationToken).ConfigureAwait(false)) + try + { + if (response.StatusCode != HttpStatusCode.OK) + return null; + + await response.Content.LoadIntoBufferAsync().ConfigureAwait(false); + var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + if (string.IsNullOrEmpty(data)) + return null; + + if (FwVersionInfo.Match(data) is Match m && m.Success) + { + var ver = m.Groups["version"].Value; + if (!string.IsNullOrEmpty(ver) && ver.Length > 4) + { + if (ver.EndsWith("00")) + ver = ver.Substring(0, 4); //4.85 + else + ver = ver.Substring(0, 4) + "." + ver.Substring(4).TrimEnd('0'); //4.851 -> 4.85.1 + } + return new FirmwareInfo { Version = ver, DownloadUrl = m.Groups["url"].Value, Locale = fwLocale}; + } + + return null; + } + catch (Exception e) + { + ConsoleLogger.PrintError(e, response); + return null; + } + } + catch (Exception e) + { + ApiConfig.Log.Error(e, "Failed to GET " + uri); + return null; + } + } } } diff --git a/CompatBot/Commands/Misc.cs b/CompatBot/Commands/Misc.cs index ee3e0489..72190a70 100644 --- a/CompatBot/Commands/Misc.cs +++ b/CompatBot/Commands/Misc.cs @@ -418,6 +418,10 @@ namespace CompatBot.Commands return Psn.SearchForGame(ctx, game, 3); } + [Command("firmware"), Aliases("fw"), Cooldown(1, 10, CooldownBucketType.Channel)] + [Description("Checks for latest PS3 firmware version")] + public Task Firmware(CommandContext ctx) => Psn.Check.GetFirmwareAsync(ctx); + [Command("compare"), Hidden] [Description("Calculates the similarity metric of two phrases from 0 (completely different) to 1 (identical)")] public Task Compare(CommandContext ctx, string strA, string strB) diff --git a/CompatBot/Commands/Psn.Check.cs b/CompatBot/Commands/Psn.Check.cs index 8f525e0f..c3c5ceeb 100644 --- a/CompatBot/Commands/Psn.Check.cs +++ b/CompatBot/Commands/Psn.Check.cs @@ -116,6 +116,18 @@ namespace CompatBot.Commands await ctx.ReactWithAsync(Config.Reactions.Success, $"Added {itemsToCheck.Count} ID{StringUtils.GetSuffix(itemsToCheck.Count)} to the scraping queue").ConfigureAwait(false); } + + [Command("firmware"), Aliases("fw")] + [Cooldown(1, 10, CooldownBucketType.Channel)] + [Description("Checks for latest PS3 firmware version")] + public Task Firmware(CommandContext ctx) => GetFirmwareAsync(ctx); + + internal static async Task GetFirmwareAsync(CommandContext ctx) + { + var fwList = await Client.GetHighestFwVersionAsync(Config.Cts.Token).ConfigureAwait(false); + var embed = fwList.ToEmbed(); + await ctx.RespondAsync(embed: embed).ConfigureAwait(false); + } } } } diff --git a/CompatBot/Utils/ResultFormatters/FwInfoFormatter.cs b/CompatBot/Utils/ResultFormatters/FwInfoFormatter.cs new file mode 100644 index 00000000..c3ace1e9 --- /dev/null +++ b/CompatBot/Utils/ResultFormatters/FwInfoFormatter.cs @@ -0,0 +1,67 @@ +ο»Ώusing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using DSharpPlus.Entities; +using PsnClient.POCOs; + +namespace CompatBot.Utils.ResultFormatters +{ + internal static class FwInfoFormatter + { + //2019_0828_c975768e5d70e105a72656f498cc9be9/PS3UPDAT.PUP + private static readonly Regex fwLinkInfo = new Regex(@"(?\d{4})_(?\d\d)(?\d\d)_(?[0-9a-f]+)", + RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnoreCase); + private static readonly Dictionary RegionToFlagMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase) + { + ["us"] = "πŸ‡ΊπŸ‡Έ", + ["eu"] = "πŸ‡ͺπŸ‡Ί", + ["uk"] = "πŸ‡¬πŸ‡§", + ["au"] = "πŸ‡¦πŸ‡Ί", + ["ru"] = "πŸ‡·πŸ‡Ί", + ["jp"] = "πŸ‡―πŸ‡΅", + ["br"] = "πŸ‡§πŸ‡·", + ["cn"] = "πŸ‡¨πŸ‡³", + ["hk"] = "πŸ‡­πŸ‡°", + ["mx"] = "πŸ‡²πŸ‡½", + ["sa"] = "πŸ‡ΈπŸ‡¦", + ["tw"] = "πŸ‡ΉπŸ‡Ό", + ["kr"] = "πŸ‡°πŸ‡·", + }; + + public static DiscordEmbedBuilder ToEmbed(this List fwInfoList) + { + var result = new DiscordEmbedBuilder() + .WithTitle("PS3 Firmware Information") + .WithColor(Config.Colors.DownloadLinks); + + if (fwInfoList?.Count > 0) + { + var info = fwInfoList.Select(fwi => fwLinkInfo.Match(fwi.DownloadUrl)).FirstOrDefault(m => m.Success); + if (info != null) + { + result.Description = $"Latest version is **{fwInfoList[0].Version}** released on {info.Groups["year"].Value}-{info.Groups["month"].Value}-{info.Groups["day"].Value}\n" + + $"It is available in {fwInfoList.Count} region{(fwInfoList.Count == 1 ? "" : "s")} out of {RegionToFlagMap.Count}"; + result.AddField("Checksums", $"MD5: `{info.Groups["md5"].Value}`\n" + + "You can use [HashCheck](https://github.com/gurnec/HashCheck/releases/latest) to verify your download"); + var links = new StringBuilder(); + foreach (var fwi in fwInfoList) + { + var newLink = $"[{RegionToFlagMap[fwi.Locale]}]({fwi.DownloadUrl}) "; + if (links.Length + newLink.Length > EmbedPager.MaxFieldLength) + break; + + links.Append(newLink); + } + result.AddField("System Software License Agreement", "You **must** read and agree with the terms described [here](https://doc.dl.playstation.net/doc/ps3-eula/) before downloading the update"); + result.AddField("Download links", links.ToString().TrimEnd()); + return result.WithFooter("Every region has identical firmware content"); + } + } + + return result + .WithColor(Config.Colors.CompatStatusUnknown); + } + } +}