mirror of
https://github.com/RPCS3/discord-bot.git
synced 2026-01-31 01:25:22 +01:00
!download command to search for games
This commit is contained in:
@@ -5,19 +5,19 @@
|
||||
// api endpoints, oauth, oauth authorize, telemetry, localization options, billing template, locales, country names, topup settings, paypal sandbox settings, gct, apm, sofort, ...
|
||||
|
||||
// this is item #6 in App array
|
||||
public class AppLocales
|
||||
public sealed class AppLocales
|
||||
{
|
||||
public string[] EnabledLocales; // "ar-AE",...
|
||||
public AppLocaleOverride[] Overrides;
|
||||
}
|
||||
|
||||
public class AppLocaleOverride
|
||||
public sealed class AppLocaleOverride
|
||||
{
|
||||
public AppLocaleOverrideCriteria Criteria;
|
||||
public string GensenLocale; // "ar-AE"
|
||||
}
|
||||
|
||||
public class AppLocaleOverrideCriteria
|
||||
public sealed class AppLocaleOverrideCriteria
|
||||
{
|
||||
public string Language; // "ar"
|
||||
public string Country; // "AE|BH|KW|LB|OM|QA|SA"
|
||||
|
||||
@@ -22,7 +22,10 @@ namespace PsnClient.POCOs
|
||||
public bool? NsxPsPlusUpsell;
|
||||
public int? TemplateId;
|
||||
public string ThumbnailUrlBase;
|
||||
public int? Start;
|
||||
public int? Size;
|
||||
public int TotalResults;
|
||||
public string Query;
|
||||
public ContainerBanner[] Banners;
|
||||
public ContainerFacet[] Facets;
|
||||
public ContainerPromoBackground[] PromoBackgrounds;
|
||||
|
||||
@@ -302,6 +302,37 @@ namespace PsnClient
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Container> SearchAsync(string locale, string search, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var loc = locale.AsLocaleData();
|
||||
var searchId = Uri.EscapeUriString(search);
|
||||
var queryId = Uri.EscapeDataString(searchId);
|
||||
var uri = new Uri($"https://store.playstation.com/valkyrie-api/{loc.language}/{loc.country}/999/faceted-search/{searchId}?query={queryId}&game_content_type=games&size=30&bucket=games&platform=ps3&start=0");
|
||||
using (var message = new HttpRequestMessage(HttpMethod.Get, uri))
|
||||
using (var response = await client.SendAsync(message, cancellationToken).ConfigureAwait(false))
|
||||
try
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
return null;
|
||||
|
||||
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
|
||||
return await response.Content.ReadAsAsync<Container>(dashedFormatters, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ConsoleLogger.PrintError(e, response);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ApiConfig.Log.Error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetSessionCookies(string locale, CancellationToken cancellationToken)
|
||||
{
|
||||
var loc = locale.AsLocaleData();
|
||||
|
||||
@@ -138,7 +138,7 @@ Example usage:
|
||||
await ch.SendMessageAsync(embed: embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Group("latest"), Aliases("download"), TriggersTyping]
|
||||
[Group("latest"), TriggersTyping]
|
||||
[Description("Provides links to the latest RPCS3 build")]
|
||||
[Cooldown(1, 30, CooldownBucketType.Channel)]
|
||||
public sealed class UpdatesCheck: BaseCommandModuleCustom
|
||||
|
||||
@@ -338,5 +338,10 @@ namespace CompatBot.Commands
|
||||
var ch = await ctx.GetChannelForSpamAsync().ConfigureAwait(false);
|
||||
await ch.SendMessageAsync($"{ctx.User.Mention} congratulations, you're the meme").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("download"), Cooldown(1, 20, CooldownBucketType.Channel)]
|
||||
[Description("Find games to download")]
|
||||
public Task Download(CommandContext ctx, [RemainingText] string game)
|
||||
=> Psn.SearchForGame(ctx, game);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,11 @@ using DSharpPlus.CommandsNext;
|
||||
using DSharpPlus.CommandsNext.Attributes;
|
||||
using DSharpPlus.Entities;
|
||||
using DSharpPlus.Interactivity;
|
||||
using PsnClient;
|
||||
|
||||
namespace CompatBot.Commands
|
||||
{
|
||||
internal sealed partial class Psn
|
||||
{
|
||||
private static readonly Client Client = new Client();
|
||||
|
||||
[Group("check")]
|
||||
[Description("Commands to check for various stuff on PSN")]
|
||||
public sealed class Check: BaseCommandModuleCustom
|
||||
|
||||
@@ -4,9 +4,16 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CompatBot.Commands.Attributes;
|
||||
using CompatBot.Database;
|
||||
using CompatBot.Database.Providers;
|
||||
using CompatBot.Utils;
|
||||
using DSharpPlus.CommandsNext;
|
||||
using DSharpPlus.CommandsNext.Attributes;
|
||||
using DSharpPlus.Entities;
|
||||
using DSharpPlus.Interactivity;
|
||||
using Google.Apis.Http;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using PsnClient;
|
||||
using PsnClient.POCOs;
|
||||
|
||||
namespace CompatBot.Commands
|
||||
{
|
||||
@@ -14,6 +21,8 @@ namespace CompatBot.Commands
|
||||
[Description("Commands related to PSN metadata")]
|
||||
internal sealed partial class Psn: BaseCommandModuleCustom
|
||||
{
|
||||
private static readonly Client Client = new Client();
|
||||
|
||||
[Command("fix"), RequiresBotModRole]
|
||||
[Description("Reset thumbnail cache for specified product")]
|
||||
public async Task Fix(CommandContext ctx, [Description("Product ID to reset")] string productId)
|
||||
@@ -47,6 +56,74 @@ namespace CompatBot.Commands
|
||||
await ctx.ReactWithAsync(Config.Reactions.Success, "Reset state timestamps").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Command("search")]
|
||||
[Description("Provides game information from PSN")]
|
||||
public Task Search(CommandContext ctx, [RemainingText] string search)
|
||||
=> SearchForGame(ctx, search);
|
||||
|
||||
public static async Task SearchForGame(CommandContext ctx, [RemainingText] string search)
|
||||
{
|
||||
DiscordMessage msg = null;
|
||||
if (string.IsNullOrEmpty(search))
|
||||
{
|
||||
var interact = ctx.Client.GetInteractivity();
|
||||
msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "What game are you looking for?").ConfigureAwait(false);
|
||||
var response = await interact.WaitForMessageAsync(m => m.Author == ctx.User).ConfigureAwait(false);
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
msg = null;
|
||||
if (string.IsNullOrEmpty(response?.Message?.Content))
|
||||
{
|
||||
await ctx.ReactWithAsync(Config.Reactions.Failure).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
search = response.Message.Content;
|
||||
}
|
||||
var msgTask = msg.UpdateOrCreateMessageAsync(ctx.Channel, "Searching...");
|
||||
var psnResponseUSTask = Client.SearchAsync("en-US", search, Config.Cts.Token);
|
||||
var psnResponseEUTask = Client.SearchAsync("en-GB", search, Config.Cts.Token);
|
||||
var psnResponseJPTask = Client.SearchAsync("ja-JP", search, 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);
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
msg = null;
|
||||
var usGame = GetBestMatch(responseUS.Included, search);
|
||||
var euGame = GetBestMatch(responseEU.Included, search);
|
||||
var jpGame = GetBestMatch(responseJP.Included, search);
|
||||
var hasResults = false;
|
||||
foreach (var (g, region, locale) in new[]{(usGame, "US", "en-US"), (euGame, "EU", "en-GB"), (jpGame, "JP", "ja-JP")}.Where(i => i.Item1 != null))
|
||||
{
|
||||
var thumb = await ThumbnailProvider.GetEmbeddableUrlAsync(ctx.Client, g.Id, g.Attributes.ThumbnailUrlBase).ConfigureAwait(false);
|
||||
var score = g.Attributes.StarRating?.Score == null ? "N/A" : $"{StringUtils.GetStars(g.Attributes.StarRating?.Score)} ({g.Attributes.StarRating?.Score})";
|
||||
var result = new DiscordEmbedBuilder
|
||||
{
|
||||
Color = new DiscordColor(0x0071cd),
|
||||
Title = $"⏬ {g.Attributes.Name} [{region}] ({g.Attributes.FileSize?.Value} {g.Attributes.FileSize?.Unit})",
|
||||
Url = $"https://store.playstation.com/{locale}/product/{g.Id}",
|
||||
Description = $"Rating: {score}",
|
||||
ThumbnailUrl = thumb.url,
|
||||
};
|
||||
hasResults = true;
|
||||
await ctx.RespondAsync(embed: result).ConfigureAwait(false);
|
||||
}
|
||||
if (!hasResults)
|
||||
await msg.UpdateOrCreateMessageAsync(ctx.Channel, "No results").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static ContainerIncluded GetBestMatch(ContainerIncluded[] included, string search)
|
||||
{
|
||||
return (
|
||||
from i in included
|
||||
where (i.Type == "game" || i.Type == "legacy-sku") && (i.Attributes.TopCategory != "demo" && i.Attributes.GameContentType != "Demo")
|
||||
let m = new {score = search.GetFuzzyCoefficientCached(i.Attributes.Name), item = i}
|
||||
where m.score > 0.3 || (i.Attributes.Name?.StartsWith(search, StringComparison.InvariantCultureIgnoreCase) ?? false)
|
||||
orderby m.score descending
|
||||
select m.item
|
||||
).FirstOrDefault();
|
||||
}
|
||||
|
||||
private static async Task TryDeleteThumbnailCache(CommandContext ctx, List<(string contentId, string link)> linksToRemove)
|
||||
{
|
||||
var contentIds = linksToRemove.ToDictionary(l => l.contentId, l => l.link);
|
||||
|
||||
@@ -125,5 +125,34 @@ namespace CompatBot.Database.Providers
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<(string url, byte[] image)> GetEmbeddableUrlAsync(DiscordClient client, string contentId, string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var imgStream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false))
|
||||
using (var memStream = new MemoryStream())
|
||||
{
|
||||
await imgStream.CopyToAsync(memStream).ConfigureAwait(false);
|
||||
// minimum jpg size is 119 bytes, png is 67 bytes
|
||||
if (memStream.Length < 64)
|
||||
return (null, null);
|
||||
|
||||
memStream.Seek(0, SeekOrigin.Begin);
|
||||
var spam = await client.GetChannelAsync(Config.ThumbnailSpamId).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(Path.GetExtension(url)))
|
||||
{
|
||||
var message = await spam.SendFileAsync(contentId + ".jpg", memStream, contentId).ConfigureAwait(false);
|
||||
url = message.Attachments.First().Url;
|
||||
}
|
||||
return (url, memStream.ToArray());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Config.Log.Warn(e);
|
||||
}
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +170,32 @@ namespace CompatBot.Utils
|
||||
return s.PadRight(totalWidth, padding);
|
||||
}
|
||||
|
||||
public static string GetStars(decimal? stars)
|
||||
{
|
||||
if (!stars.HasValue)
|
||||
return null;
|
||||
|
||||
var fullStars = (int)stars;
|
||||
var halfStar = Math.Round((stars.Value - fullStars)*4, MidpointRounding.ToEven);
|
||||
var noStars = 5 - (halfStar > 0 && halfStar < 4 ? 1 : 0) - fullStars;
|
||||
var result = "";
|
||||
for (var i = 0; i < fullStars; i++)
|
||||
result += "🌕";
|
||||
|
||||
if (halfStar > 3)
|
||||
;
|
||||
else if (halfStar > 2)
|
||||
result += "🌖";
|
||||
else if (halfStar > 1)
|
||||
result += "🌗";
|
||||
else if (halfStar > 0)
|
||||
result += "🌘";
|
||||
|
||||
for (var i = 0; i < noStars; i++)
|
||||
result += "🌑";
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool IsFormat(char c) => SpaceCharacters.Contains(c);
|
||||
|
||||
private static string CreateTrimmedString(string str, int start, int end)
|
||||
|
||||
Reference in New Issue
Block a user