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"); } }
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();
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); } }
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); } }
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); } }
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..];
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); } }
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); } }