Exemple #1
0
            public async Task Updates(CommandContext ctx, [RemainingText, Description("Product code such as `BLUS12345`")] string productCode)
            {
                var            providedId = productCode;
                var            id         = ProductCodeLookup.GetProductIds(productCode).FirstOrDefault();
                var            askForId   = true;
                DiscordMessage?botMsg     = null;

                if (string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(productCode))
                {
                    var requestBuilder = RequestBuilder.Start().SetSearch(productCode);
                    var compatResult   = CompatList.GetLocalCompatResult(requestBuilder)
                                         .GetSortedList()
                                         .Where(i => i.score > 0.8)
                                         .Take(25)
                                         .Select(i => i.code)
                                         .Batch(5)
                                         .ToList();
                    if (compatResult.Count > 0)
                    {
                        askForId = false;
                        var messageBuilder = new DiscordMessageBuilder()
                                             .WithContent("Please select correct product code from the list or specify your own")
                                             .WithReply(ctx.Message.Id);
                        foreach (var row in compatResult)
                        {
                            messageBuilder.AddComponents(row.Select(c => new DiscordButtonComponent(ButtonStyle.Secondary, "psn:check:updates:" + c, c)));
                        }
                        var interactivity = ctx.Client.GetInteractivity();
                        botMsg = await botMsg.UpdateOrCreateMessageAsync(ctx.Channel, messageBuilder).ConfigureAwait(false);

                        var reaction = await interactivity.WaitForMessageOrButtonAsync(botMsg, ctx.User, TimeSpan.FromMinutes(1)).ConfigureAwait(false);

                        if (reaction.reaction?.Id is { Length : > 0 } selectedId)
                        {
                            id = selectedId[^ 9..];
        public async Task Compat(CommandContext ctx, [RemainingText, Description("Game title to look up")] string title)
        {
            title = title?.TrimEager().Truncate(40);
            if (string.IsNullOrEmpty(title))
            {
                var prompt = await ctx.RespondAsync($"{ctx.Message.Author.Mention} what game do you want to check?").ConfigureAwait(false);

                var interact = ctx.Client.GetInteractivity();
                var response = await interact.WaitForMessageAsync(m => m.Author == ctx.Message.Author && m.Channel == ctx.Channel).ConfigureAwait(false);

                if (string.IsNullOrEmpty(response.Result?.Content) || response.Result.Content.StartsWith(Config.CommandPrefix))
                {
                    await prompt.ModifyAsync("You should specify what you're looking for").ConfigureAwait(false);

                    return;
                }

                DeletedMessagesMonitor.RemovedByBotCache.Set(prompt.Id, true, DeletedMessagesMonitor.CacheRetainTime);
                await prompt.DeleteAsync().ConfigureAwait(false);

                title = response.Result.Content.TrimEager().Truncate(40);
            }

            if (!await DiscordInviteFilter.CheckMessageForInvitesAsync(ctx.Client, ctx.Message).ConfigureAwait(false))
            {
                return;
            }

            if (!await ContentFilter.IsClean(ctx.Client, ctx.Message).ConfigureAwait(false))
            {
                return;
            }

            var productCodes = ProductCodeLookup.GetProductIds(ctx.Message.Content);

            if (productCodes.Any())
            {
                await ProductCodeLookup.LookupAndPostProductCodeEmbedAsync(ctx.Client, ctx.Message, productCodes).ConfigureAwait(false);

                return;
            }

            try
            {
                var requestBuilder = RequestBuilder.Start().SetSearch(title);
                await DoRequestAndRespond(ctx, requestBuilder).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                Config.Log.Error(e, "Failed to get compat list info");
            }
        }
Exemple #3
0
        public static async Task SearchForGame(CommandContext ctx, string search, int maxResults)
        {
            var ch = await ctx.GetChannelForSpamAsync().ConfigureAwait(false);

            DiscordMessage msg = null;

            try
            {
                if (string.IsNullOrEmpty(search))
                {
                    var interact = ctx.Client.GetInteractivity();
                    msg = await msg.UpdateOrCreateMessageAsync(ch, "What game are you looking for?").ConfigureAwait(false);

                    var response = await interact.WaitForMessageAsync(m => m.Author == ctx.User && m.Channel == ch).ConfigureAwait(false);

                    await msg.DeleteAsync().ConfigureAwait(false);

                    msg = null;
                    if (string.IsNullOrEmpty(response.Result?.Content))
                    {
                        await ctx.ReactWithAsync(Config.Reactions.Failure).ConfigureAwait(false);

                        return;
                    }

                    search = response.Result.Content;
                }

                string titleId    = null;
                var    productIds = ProductCodeLookup.GetProductIds(search);
                if (productIds.Count > 0)
                {
                    using var db = new ThumbnailDb();
                    var contentId = await db.Thumbnail.FirstOrDefaultAsync(t => t.ProductCode == productIds[0].ToUpperInvariant()).ConfigureAwait(false);

                    if (contentId?.ContentId != null)
                    {
                        titleId = contentId.ContentId;
                    }
                    if (contentId?.Name != null)
                    {
                        search = contentId.Name;
                    }
                }

                var alteredSearch = search.Trim();
                if (alteredSearch.EndsWith("demo", StringComparison.InvariantCultureIgnoreCase))
                {
                    alteredSearch = alteredSearch[..^ 4].TrimEnd();
Exemple #4
0
            public async Task Updates(CommandContext ctx, [RemainingText, Description("Product ID such as `BLUS12345`")] string productId)
            {
                productId = ProductCodeLookup.GetProductIds(productId).FirstOrDefault();
                if (string.IsNullOrEmpty(productId))
                {
                    await ctx.ReactWithAsync(Config.Reactions.Failure, $"`{productId.Sanitize()}` is not a valid product ID").ConfigureAwait(false);

                    return;
                }

                var updateInfo = await Client.GetTitleUpdatesAsync(productId, Config.Cts.Token).ConfigureAwait(false);

                var embeds = await updateInfo.AsEmbedAsync(ctx.Client, productId).ConfigureAwait(false);

                if (!ctx.Channel.IsPrivate &&
                    ctx.Message.Author.Id == 197163728867688448 &&
                    (
                        embeds[0].Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase) ||
                        embeds[0].Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase)
                    ))
                {
                    foreach (var embed in embeds)
                    {
                        var newTitle  = "(๑•ิཬ•ั๑)";
                        var partStart = embed.Title.IndexOf(" [Part");
                        if (partStart > -1)
                        {
                            newTitle += embed.Title.Substring(partStart);
                        }
                        embed.Title = newTitle;
                        if (!string.IsNullOrEmpty(embed.ThumbnailUrl))
                        {
                            embed.ThumbnailUrl = "https://cdn.discordapp.com/attachments/417347469521715210/516340151589535745/onionoff.png";
                        }
                    }
                    var sqvat = ctx.Client.GetEmoji(":sqvat:", Config.Reactions.No);
                    await ctx.Message.ReactWithAsync(ctx.Client, sqvat).ConfigureAwait(false);
                }
                if (embeds.Count > 1 || embeds[0].Fields?.Count > 0)
                {
                    embeds[embeds.Count - 1] = embeds.Last().WithFooter("Note that you need to install all listed updates one by one");
                }
                foreach (var embed in embeds)
                {
                    await ctx.RespondAsync(embed : embed).ConfigureAwait(false);
                }
            }
Exemple #5
0
            public async Task Updates(CommandContext ctx, [RemainingText, Description("Product ID such as `BLUS12345`")] string productId)
            {
                productId = ProductCodeLookup.GetProductIds(productId).FirstOrDefault();
                if (string.IsNullOrEmpty(productId))
                {
                    await ctx.ReactWithAsync(Config.Reactions.Failure, $"`{productId.Sanitize()}` is not a valid product ID").ConfigureAwait(false);

                    return;
                }

                var updateInfo = await Client.GetTitleUpdatesAsync(productId, Config.Cts.Token).ConfigureAwait(false);

                var embeds = await updateInfo.AsEmbedAsync(ctx.Client, productId).ConfigureAwait(false);

                if (embeds.Count > 1 || embeds[0].Fields?.Count > 0)
                {
                    embeds[embeds.Count - 1] = embeds.Last().WithFooter("Note that you need to install all listed updates one by one");
                }
                foreach (var embed in embeds)
                {
                    await ctx.RespondAsync(embed : embed).ConfigureAwait(false);
                }
            }
Exemple #6
0
            public async Task Updates(CommandContext ctx, [RemainingText, Description("Product code such as `BLUS12345`")] string productCode)
            {
                var id = ProductCodeLookup.GetProductIds(productCode).FirstOrDefault();

                if (string.IsNullOrEmpty(id))
                {
                    var botMsg = await ctx.RespondAsync("Please specify a valid product code (e.g. BLUS12345 or NPEB98765):").ConfigureAwait(false);

                    var interact = ctx.Client.GetInteractivity();
                    var msg      = await interact.WaitForMessageAsync(m => m.Author == ctx.User && m.Channel == ctx.Channel && !string.IsNullOrEmpty(m.Content)).ConfigureAwait(false);

                    await botMsg.DeleteAsync().ConfigureAwait(false);

                    if (string.IsNullOrEmpty(msg.Result?.Content))
                    {
                        return;
                    }

                    if (msg.Result.Content.StartsWith(Config.CommandPrefix) || msg.Result.Content.StartsWith(Config.AutoRemoveCommandPrefix))
                    {
                        return;
                    }

                    id = ProductCodeLookup.GetProductIds(msg.Result.Content).FirstOrDefault();
                    if (string.IsNullOrEmpty(id))
                    {
                        await ctx.ReactWithAsync(Config.Reactions.Failure, $"`{msg.Result.Content.Trim(10).Sanitize(replaceBackTicks: true)}` is not a valid product code").ConfigureAwait(false);

                        return;
                    }
                }

                List <DiscordEmbedBuilder> embeds;

                try
                {
                    var updateInfo = await Client.GetTitleUpdatesAsync(id, Config.Cts.Token).ConfigureAwait(false);

                    embeds = await updateInfo.AsEmbedAsync(ctx.Client, id).ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    Config.Log.Warn(e, "Failed to get title update info");
                    embeds = new List <DiscordEmbedBuilder>
                    {
                        new DiscordEmbedBuilder
                        {
                            Color       = Config.Colors.Maintenance,
                            Title       = "Service is unavailable",
                            Description = "There was an error communicating with the service. Try again in a few minutes.",
                        }
                    };
                }

                if (!ctx.Channel.IsPrivate &&
                    ctx.Message.Author.Id == 197163728867688448 &&
                    (
                        embeds[0].Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase) ||
                        embeds[0].Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase)
                    ))
                {
                    foreach (var embed in embeds)
                    {
                        var newTitle  = "(๑•ิཬ•ั๑)";
                        var partStart = embed.Title.IndexOf(" [Part");
                        if (partStart > -1)
                        {
                            newTitle += embed.Title.Substring(partStart);
                        }
                        embed.Title = newTitle;
                        if (!string.IsNullOrEmpty(embed.ThumbnailUrl))
                        {
                            embed.ThumbnailUrl = "https://cdn.discordapp.com/attachments/417347469521715210/516340151589535745/onionoff.png";
                        }
                    }
                    var sqvat = ctx.Client.GetEmoji(":sqvat:", Config.Reactions.No);
                    await ctx.Message.ReactWithAsync(sqvat).ConfigureAwait(false);
                }
                if (embeds.Count > 1 || embeds[0].Fields?.Count > 0)
                {
                    embeds[embeds.Count - 1] = embeds.Last().WithFooter("Note that you need to install all listed updates one by one");
                }
                foreach (var embed in embeds)
                {
                    await ctx.RespondAsync(embed : embed).ConfigureAwait(false);
                }
            }
Exemple #7
0
            public async Task Updates(CommandContext ctx, [RemainingText, Description("Product code such as `BLUS12345`")] string productCode)
            {
                var id = ProductCodeLookup.GetProductIds(productCode).FirstOrDefault();

                if (string.IsNullOrEmpty(id))
                {
                    var botMsg = await ctx.Channel.SendMessageAsync("Please specify a valid product code (e.g. BLUS12345 or NPEB98765):").ConfigureAwait(false);

                    var interact = ctx.Client.GetInteractivity();
                    var msg      = await interact.WaitForMessageAsync(m => m.Author == ctx.User && m.Channel == ctx.Channel && !string.IsNullOrEmpty(m.Content)).ConfigureAwait(false);

                    await botMsg.DeleteAsync().ConfigureAwait(false);

                    if (string.IsNullOrEmpty(msg.Result?.Content))
                    {
                        return;
                    }

                    if (msg.Result.Content.StartsWith(Config.CommandPrefix) || msg.Result.Content.StartsWith(Config.AutoRemoveCommandPrefix))
                    {
                        return;
                    }

                    id = ProductCodeLookup.GetProductIds(msg.Result.Content).FirstOrDefault();
                    if (string.IsNullOrEmpty(id))
                    {
                        await ctx.ReactWithAsync(Config.Reactions.Failure, $"`{msg.Result.Content.Trim(10).Sanitize(replaceBackTicks: true)}` is not a valid product code").ConfigureAwait(false);

                        return;
                    }
                }

                List <DiscordEmbedBuilder> embeds;

                try
                {
                    var updateInfo = await TitleUpdateInfoProvider.GetAsync(id, Config.Cts.Token).ConfigureAwait(false);

                    embeds = await updateInfo.AsEmbedAsync(ctx.Client, id).ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    Config.Log.Warn(e, "Failed to get title update info");
                    embeds = new()
                    {
                        new()
                        {
                            Color       = Config.Colors.Maintenance,
                            Title       = "Service is unavailable",
                            Description = "There was an error communicating with the service. Try again in a few minutes.",
                        }
                    };
                }

                if (ctx.IsOnionLike() &&
                    (embeds[0].Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase) ||
                     embeds[0].Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase)))
                {
                    foreach (var embed in embeds)
                    {
                        var newTitle  = "(๑•ิཬ•ั๑)";
                        var partStart = embed.Title.IndexOf(" [Part", StringComparison.Ordinal);
                        if (partStart > -1)
                        {
                            newTitle += embed.Title[partStart..];
Exemple #8
0
        public async Task Search(CommandContext ctx, [RemainingText, Description("Product ID, module, or function name. **Case sensitive**")] string search)
        {
            if (string.IsNullOrEmpty(search))
            {
                await ctx.ReactWithAsync(Config.Reactions.Failure, "No meaningful search query provided").ConfigureAwait(false);

                return;
            }

            var productCodes = ProductCodeLookup.GetProductIds(search);

            if (productCodes.Any())
            {
                await ReturnSyscallsByGameAsync(ctx, productCodes.First()).ConfigureAwait(false);

                return;
            }

            if (ctx.User.Id == 216724245957312512UL && !search.StartsWith("sys_", StringComparison.InvariantCultureIgnoreCase))
            {
                await ctx.RespondAsync($"This is not a _syscall_, {ctx.User.Mention}").ConfigureAwait(false);

                return;
            }

            using var db = new ThumbnailDb();
            if (db.SyscallInfo.Any(sci => sci.Module == search || sci.Function == search))
            {
                var productInfoList = db.SyscallToProductMap.AsNoTracking()
                                      .Where(m => m.SyscallInfo.Module == search || m.SyscallInfo.Function == search)
                                      .Select(m => new { m.Product.ProductCode, Name = m.Product.Name.StripMarks() ?? "???" })
                                      .Distinct()
                                      .ToList();
                var groupedList = productInfoList
                                  .GroupBy(m => m.Name, m => m.ProductCode, StringComparer.InvariantCultureIgnoreCase)
                                  .OrderBy(g => g.Key, StringComparer.OrdinalIgnoreCase)
                                  .ToList();
                if (groupedList.Any())
                {
                    var bigList = groupedList.Count >= Config.MaxSyscallResultLines;

                    var result   = new StringBuilder();
                    var fullList = bigList ? new StringBuilder() : null;
                    result.AppendLine($"List of games using `{search}`:```");
                    var c = 0;
                    foreach (var gi in groupedList)
                    {
                        var productIds = string.Join(", ", gi.Distinct().OrderBy(pc => pc).AsEnumerable());
                        if (c < Config.MaxSyscallResultLines)
                        {
                            result.AppendLine($"{gi.Key.Trim(60)} [{productIds}]");
                        }
                        if (bigList)
                        {
                            fullList.AppendLine($"{gi.Key} [{productIds}]");
                        }
                        c++;
                    }
                    await ctx.SendAutosplitMessageAsync(result.Append("```")).ConfigureAwait(false);

                    if (bigList)
                    {
                        using var memoryStream = new MemoryStream((int)(fullList.Capacity * 1.25));
                        using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8);
                        await streamWriter.WriteAsync(fullList).ConfigureAwait(false);

                        await streamWriter.FlushAsync().ConfigureAwait(false);

                        memoryStream.Seek(0, SeekOrigin.Begin);
                        await ctx.RespondWithFileAsync($"{search}.txt", memoryStream, $"See attached file for full list of {groupedList.Count} entries").ConfigureAwait(false);
                    }
                }
                else
                {
                    await ctx.RespondAsync($"No games found that use `{search}`").ConfigureAwait(false);
                }
            }
            else
            {
                var result  = new StringBuilder("Unknown entity name");
                var modules = await db.SyscallInfo.Select(sci => sci.Module).Distinct().ToListAsync().ConfigureAwait(false);

                var substrModules = modules.Where(m => m.Contains(search, StringComparison.CurrentCultureIgnoreCase));
                var fuzzyModules  = modules
                                    .Select(m => (name: m, score: search.GetFuzzyCoefficientCached(m)))
                                    .Where(i => i.score > 0.6)
                                    .OrderByDescending(i => i.score)
                                    .Select(i => i.name)
                                    .ToList();
                modules = substrModules
                          .Concat(fuzzyModules)
                          .Distinct()
                          .OrderBy(m => m, StringComparer.OrdinalIgnoreCase)
                          .ToList();
                var modulesFound = modules.Any();
                if (modulesFound)
                {
                    result.AppendLine(", possible modules:```");
                    foreach (var m in modules)
                    {
                        result.AppendLine(m);
                    }
                    result.AppendLine("```");
                }

                var functions = await db.SyscallInfo.Select(sci => sci.Function).Distinct().ToListAsync().ConfigureAwait(false);

                var substrFuncs = functions.Where(f => f.Contains(search, StringComparison.InvariantCultureIgnoreCase));
                var fuzzyFuncs  = functions
                                  .Select(f => (name: f, score: search.GetFuzzyCoefficientCached(f)))
                                  .Where(i => i.score > 0.6)
                                  .OrderByDescending(i => i.score)
                                  .Select(i => i.name)
                                  .ToList();
                functions = substrFuncs
                            .Concat(fuzzyFuncs)
                            .Distinct()
                            .OrderBy(f => f, StringComparer.OrdinalIgnoreCase)
                            .ToList();
                var functionsFound = functions.Any();
                if (functionsFound)
                {
                    if (modulesFound)
                    {
                        result.AppendLine("Possible functions:```");
                    }
                    else
                    {
                        result.AppendLine(", possible functions:```");
                    }
                    foreach (var f in functions)
                    {
                        result.AppendLine(f);
                    }
                    result.AppendLine("```");
                }
                await ctx.SendAutosplitMessageAsync(result).ConfigureAwait(false);
            }
        }
Exemple #9
0
        public static async Task SearchForGame(CommandContext ctx, string search, int maxResults)
        {
            var ch = await ctx.GetChannelForSpamAsync().ConfigureAwait(false);

            DiscordMessage msg = null;

            try
            {
                if (string.IsNullOrEmpty(search))
                {
                    var interact = ctx.Client.GetInteractivity();
                    msg = await msg.UpdateOrCreateMessageAsync(ch, "What game are you looking for?").ConfigureAwait(false);

                    var response = await interact.WaitForMessageAsync(m => m.Author == ctx.User && m.Channel == ch).ConfigureAwait(false);

                    await msg.DeleteAsync().ConfigureAwait(false);

                    msg = null;
                    if (string.IsNullOrEmpty(response.Result?.Content))
                    {
                        await ctx.ReactWithAsync(Config.Reactions.Failure).ConfigureAwait(false);

                        return;
                    }

                    search = response.Result.Content;
                }

                string titleId    = null;
                var    productIds = ProductCodeLookup.GetProductIds(search);
                if (productIds.Count > 0)
                {
                    using (var db = new ThumbnailDb())
                    {
                        var contentId = await db.Thumbnail.FirstOrDefaultAsync(t => t.ProductCode == productIds[0].ToUpperInvariant()).ConfigureAwait(false);

                        if (contentId?.ContentId != null)
                        {
                            titleId = contentId.ContentId;
                        }
                        if (contentId?.Name != null)
                        {
                            search = contentId.Name;
                        }
                    }
                }

                var alteredSearch = search.Trim();
                if (alteredSearch.EndsWith("demo", StringComparison.InvariantCultureIgnoreCase))
                {
                    alteredSearch = alteredSearch.Substring(0, alteredSearch.Length - 4).TrimEnd();
                }
                if (alteredSearch.EndsWith("trial", StringComparison.InvariantCultureIgnoreCase))
                {
                    alteredSearch = alteredSearch.Substring(0, alteredSearch.Length - 5).TrimEnd();
                }
                if (alteredSearch.EndsWith("体験版"))
                {
                    alteredSearch = alteredSearch.Substring(0, alteredSearch.Length - 3).TrimEnd();
                }

                if (string.IsNullOrEmpty(alteredSearch))
                {
                    alteredSearch = search;
                }
                var msgTask           = msg.UpdateOrCreateMessageAsync(ch, "⏳ Searching...");
                var psnResponseUSTask = titleId == null?Client.SearchAsync("en-US", alteredSearch, Config.Cts.Token) : Client.ResolveContentAsync("en-US", titleId, 1, Config.Cts.Token);

                var psnResponseEUTask = titleId == null?Client.SearchAsync("en-GB", alteredSearch, Config.Cts.Token) : Client.ResolveContentAsync("en-GB", titleId, 1, Config.Cts.Token);

                var psnResponseJPTask = titleId == null?Client.SearchAsync("ja-JP", alteredSearch, Config.Cts.Token) : Client.ResolveContentAsync("ja-JP", titleId, 1, Config.Cts.Token);

                await Task.WhenAll(msgTask, psnResponseUSTask, psnResponseEUTask, psnResponseJPTask).ConfigureAwait(false);

                var responseUS = await psnResponseUSTask.ConfigureAwait(false);

                var responseEU = await psnResponseEUTask.ConfigureAwait(false);

                var responseJP = await psnResponseJPTask.ConfigureAwait(false);

                msg = await msgTask.ConfigureAwait(false);

                msg = await msg.UpdateOrCreateMessageAsync(ch, "⌛ Preparing results...").ConfigureAwait(false);

                var usGames      = GetBestMatch(responseUS?.Included, search, maxResults) ?? EmptyMatch;
                var euGames      = GetBestMatch(responseEU?.Included, search, maxResults) ?? EmptyMatch;
                var jpGames      = GetBestMatch(responseJP?.Included, search, maxResults) ?? EmptyMatch;
                var combinedList = usGames.Select(g => (g, "US", "en-US"))
                                   .Concat(euGames.Select(g => (g, "EU", "en-GB")))
                                   .Concat(jpGames.Select(g => (g, "JP", "ja-JP")))
                                   .ToList();
                combinedList = GetSortedList(combinedList, search, maxResults);
                var hasResults = false;
                foreach (var(g, region, locale) in combinedList)
                {
                    if (g == null)
                    {
                        continue;
                    }

                    var thumb = await ThumbnailProvider.GetThumbnailUrlWithColorAsync(ctx.Client, g.Id, PsnBlue, g.Attributes.ThumbnailUrlBase).ConfigureAwait(false);

                    string score;
                    if ((g.Attributes.StarRating?.Score ?? 0m) == 0m || (g.Attributes.StarRating?.Total ?? 0) == 0)
                    {
                        score = "N/A";
                    }
                    else
                    {
                        if (ctx.User.Id == 247291873511604224ul)
                        {
                            score = StringUtils.GetStars(g.Attributes.StarRating?.Score);
                        }
                        else
                        {
                            score = StringUtils.GetMoons(g.Attributes.StarRating?.Score);
                        }
                        score = $"{score} ({g.Attributes.StarRating?.Score} by {g.Attributes.StarRating.Total} people)";
                    }
                    string fileSize = null;
                    if (g.Attributes.FileSize?.Value.HasValue ?? false)
                    {
                        fileSize = g.Attributes.FileSize.Value.ToString();
                        if (g.Attributes.FileSize?.Unit is string unit && !string.IsNullOrEmpty(unit))
                        {
                            fileSize += " " + unit;
                        }
                        else
                        {
                            fileSize += " GB";
                        }
                        fileSize = $" ({fileSize})";
                    }

                    //var instructions = g.Attributes.TopCategory == "disc_based_game" ? "dumping_procedure" : "software_distribution";
                    var result = new DiscordEmbedBuilder
                    {
                        Color       = thumb.color,
                        Title       = $"⏬ {g.Attributes.Name?.StripMarks()} [{region}]{fileSize}",
                        Url         = $"https://store.playstation.com/{locale}/product/{g.Id}",
                        Description = $"Rating: {score}\n" +
                                      "[Instructions](https://rpcs3.net/quickstart#software_distribution)",
                        ThumbnailUrl = thumb.url,
                    };
                    await ProductCodeLookup.FixAfrikaAsync(ctx.Client, ctx.Message, result).ConfigureAwait(false);

#if DEBUG
                    result.WithFooter("Test instance");
#endif
                    hasResults = true;
                    await ch.SendMessageAsync(embed : result).ConfigureAwait(false);
                }
                if (hasResults)
                {
                    await msg.DeleteAsync().ConfigureAwait(false);
                }
                else
                {
                    await msg.UpdateOrCreateMessageAsync(ch, "No results").ConfigureAwait(false);
                }
            }