mirror of
https://github.com/RPCS3/discord-bot.git
synced 2026-01-31 01:25:22 +01:00
fix compatibility commands
This commit is contained in:
@@ -13,7 +13,7 @@ internal sealed class BotMath
|
||||
|
||||
[Command("calculate"), DefaultGroupCommand]
|
||||
[Description("Math; there you go, Juhn")]
|
||||
public async ValueTask Calc(SlashCommandContext ctx, [RemainingText, Description("Math expression or `help` for syntax link")] string expression)
|
||||
public async ValueTask Calc(SlashCommandContext ctx, [Description("Math expression or `help` for syntax link")] string expression)
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel();
|
||||
if (expression.Equals("help", StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
@@ -14,7 +14,7 @@ internal sealed class BotStatus
|
||||
{
|
||||
[Command("status")]
|
||||
[Description("Bot subsystem configuration status and various runtime stats")]
|
||||
public async Task Show(SlashCommandContext ctx)
|
||||
public async ValueTask Show(SlashCommandContext ctx)
|
||||
{
|
||||
var latency = ctx.Client.GetConnectionLatency(Config.BotGuildId);
|
||||
var embed = new DiscordEmbedBuilder
|
||||
|
||||
103
CompatBot/Commands/CompatList.Top.cs
Normal file
103
CompatBot/Commands/CompatList.Top.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using CompatApiClient.Utils;
|
||||
using CompatBot.Database;
|
||||
using DSharpPlus.Commands.Processors.SlashCommands.ArgumentModifiers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CompatBot.Commands;
|
||||
|
||||
internal sealed partial class CompatList
|
||||
{
|
||||
public sealed class Top
|
||||
{
|
||||
[Command("top")]
|
||||
[Description("Top game lists based on Metacritic scores and compatibility status")]
|
||||
public async ValueTask Show(SlashCommandContext ctx,
|
||||
[Description("Number of entries in the list")] int number = 10,
|
||||
[Description("Filter by compatibility status"), SlashChoiceProvider<CompatListStatusChoiceProvider>] string status = "playable",
|
||||
[Description("Listing type"), SlashChoiceProvider<ScoreTypeChoiceProvider>] string type = "both")
|
||||
{
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel() && !ctx.Channel.IsOfftopicChannel();
|
||||
await ctx.DeferResponseAsync(ephemeral).ConfigureAwait(false);
|
||||
|
||||
status = status.ToLowerInvariant();
|
||||
type = type.ToLowerInvariant();
|
||||
number = number.Clamp(1, 100);
|
||||
var exactStatus = status.EndsWith("only");
|
||||
if (exactStatus)
|
||||
status = status[..^4];
|
||||
if (!Enum.TryParse(status, true, out CompatStatus s))
|
||||
s = CompatStatus.Playable;
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
var queryBase = db.Thumbnail.AsNoTracking();
|
||||
if (exactStatus)
|
||||
queryBase = queryBase.Where(g => g.CompatibilityStatus == s);
|
||||
else
|
||||
queryBase = queryBase.Where(g => g.CompatibilityStatus >= s);
|
||||
queryBase = queryBase.Where(g => g.Metacritic != null).Include(t => t.Metacritic);
|
||||
var query = type switch
|
||||
{
|
||||
"critic" => queryBase.Where(t => t.Metacritic!.CriticScore > 0).AsEnumerable().Select(t =>
|
||||
(title: t.Metacritic!.Title, score: t.Metacritic!.CriticScore!.Value,
|
||||
second: t.Metacritic.UserScore ?? t.Metacritic.CriticScore.Value)),
|
||||
"user" => queryBase.Where(t => t.Metacritic!.UserScore > 0).AsEnumerable().Select(t =>
|
||||
(title: t.Metacritic!.Title, score: t.Metacritic!.UserScore!.Value,
|
||||
second: t.Metacritic.CriticScore ?? t.Metacritic.UserScore.Value)),
|
||||
_ => queryBase.AsEnumerable().Select(t => (title: t.Metacritic!.Title,
|
||||
score: Math.Max(t.Metacritic.CriticScore ?? 0, t.Metacritic.UserScore ?? 0), second: (byte)0)),
|
||||
};
|
||||
var resultList = query.Where(i => i.score > 0)
|
||||
.OrderByDescending(i => i.score)
|
||||
.ThenByDescending(i => i.second)
|
||||
.Distinct()
|
||||
.Take(number)
|
||||
.ToList();
|
||||
if (resultList.Count > 0)
|
||||
{
|
||||
var result = new StringBuilder($"Best {s.ToString().ToLower()}");
|
||||
if (exactStatus)
|
||||
result.Append(" only");
|
||||
result.Append(" games");
|
||||
if (type is "critic" or "user")
|
||||
result.Append($" according to {type}s");
|
||||
result.AppendLine(":");
|
||||
foreach (var (title, score, _) in resultList)
|
||||
result.AppendLine($"`{score:00}` {title}");
|
||||
var formattedResults = AutosplitResponseHelper.AutosplitMessage(result.ToString(), blockStart: null, blockEnd: null);
|
||||
await ctx.RespondAsync(formattedResults[0], ephemeral).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await ctx.RespondAsync("Failed to generate list", ephemeral).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public class CompatListStatusChoiceProvider : IChoiceProvider
|
||||
{
|
||||
private static readonly IReadOnlyList<DiscordApplicationCommandOptionChoice> compatListStatus =
|
||||
[
|
||||
new("playable", "playable"),
|
||||
new("ingame or better", "ingame"),
|
||||
new("intro or better", "intro"),
|
||||
new("loadable or better", "loadable"),
|
||||
new("only ingame", "ingameOnly"),
|
||||
new("only intro", "introOnly"),
|
||||
new("only loadable", "loadableOnly"),
|
||||
];
|
||||
|
||||
public ValueTask<IEnumerable<DiscordApplicationCommandOptionChoice>> ProvideAsync(CommandParameter parameter)
|
||||
=> ValueTask.FromResult<IEnumerable<DiscordApplicationCommandOptionChoice>>(compatListStatus);
|
||||
}
|
||||
|
||||
public class ScoreTypeChoiceProvider : IChoiceProvider
|
||||
{
|
||||
private static readonly IReadOnlyList<DiscordApplicationCommandOptionChoice> scoreType =
|
||||
[
|
||||
new("combined", "both"),
|
||||
new("critic score", "critic"),
|
||||
new("user score", "user"),
|
||||
];
|
||||
|
||||
public ValueTask<IEnumerable<DiscordApplicationCommandOptionChoice>> ProvideAsync(CommandParameter parameter)
|
||||
=> ValueTask.FromResult<IEnumerable<DiscordApplicationCommandOptionChoice>>(scoreType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,140 +39,70 @@ internal sealed partial class CompatList
|
||||
lastUpdateInfo = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateStateKey)?.Value;
|
||||
lastFullBuildNumber = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateBuildKey)?.Value;
|
||||
//lastUpdateInfo = "8022";
|
||||
if (lastUpdateInfo is string strPr
|
||||
&& int.TryParse(strPr, out var pr))
|
||||
if (lastUpdateInfo is {Length: >0} strPr && int.TryParse(strPr, out var pr))
|
||||
{
|
||||
try
|
||||
{
|
||||
var prInfo = GithubClient.GetPrInfoAsync(pr, Config.Cts.Token).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
cachedUpdateInfo = Client.GetUpdateAsync(Config.Cts.Token, prInfo?.MergeCommitSha).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (cachedUpdateInfo?.CurrentBuild != null)
|
||||
{
|
||||
if (cachedUpdateInfo?.CurrentBuild == null)
|
||||
return;
|
||||
|
||||
cachedUpdateInfo.LatestBuild = cachedUpdateInfo.CurrentBuild;
|
||||
cachedUpdateInfo.CurrentBuild = null;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
[Command("compatibility"), TextAlias("c", "compat")]
|
||||
[Description("Searches the compatibility database, USE: !compat search term")]
|
||||
public async Task Compat(CommandContext ctx, [RemainingText, Description("Game title to look up")] string? title)
|
||||
[Command("compatibility")]
|
||||
[Description("Search the game compatibility list")]
|
||||
public async ValueTask Compat(SlashCommandContext ctx, [Description("Game title or product code to look up")] string title)
|
||||
{
|
||||
title = title?.TrimEager().Truncate(40);
|
||||
if (string.IsNullOrEmpty(title))
|
||||
if (await ContentFilter.FindTriggerAsync(FilterContext.Chat, title).ConfigureAwait(false) is not null)
|
||||
{
|
||||
var prompt = await ctx.Channel.SendMessageAsync($"{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);
|
||||
await ctx.RespondAsync("Invalid game title or product code.", true).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var ephemeral = !ctx.Channel.IsSpamChannel();
|
||||
await ctx.DeferResponseAsync(ephemeral).ConfigureAwait(false);
|
||||
|
||||
DeletedMessagesMonitor.RemovedByBotCache.Set(prompt.Id, true, DeletedMessagesMonitor.CacheRetainTime);
|
||||
await prompt.DeleteAsync().ConfigureAwait(false);
|
||||
title = response.Result.Content.TrimEager().Truncate(40);
|
||||
}
|
||||
|
||||
if (!await DiscordInviteFilter.CheckMessageInvitesAreSafeAsync(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())
|
||||
var productCodes = ProductCodeLookup.GetProductIds(title);
|
||||
if (productCodes.Count > 0)
|
||||
{
|
||||
await ProductCodeLookup.LookupAndPostProductCodeEmbedAsync(ctx.Client, ctx.Message, ctx.Channel, productCodes).ConfigureAwait(false);
|
||||
var formattedResults = await ProductCodeLookup.LookupProductCodeAndFormatAsync(ctx.Client, productCodes).ConfigureAwait(false);
|
||||
await ctx.RespondAsync(embed: formattedResults[0].builder, ephemeral: ephemeral).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
title = title.TrimEager().Truncate(40);
|
||||
var requestBuilder = RequestBuilder.Start().SetSearch(title);
|
||||
await DoRequestAndRespond(ctx, requestBuilder).ConfigureAwait(false);
|
||||
await DoRequestAndRespondAsync(ctx, ephemeral, requestBuilder).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Config.Log.Error(e, "Failed to get compat list info");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
[Command("top"), LimitedToOfftopicChannel]
|
||||
//[Cooldown(1, 5, CooldownBucketType.Channel)]
|
||||
[Description("Provides top game lists based on Metacritic and compatibility lists")]
|
||||
public async Task Top(CommandContext ctx,
|
||||
[Description("Number of entries in the list")] int number = 10,
|
||||
[Description("One of `playable`, `ingame`, `intro`, `loadable`, or `<status>Only`")] string status = "playable",
|
||||
[Description("One of `both`, `critic`, or `user`")] string scoreType = "both")
|
||||
{
|
||||
status = status.ToLowerInvariant();
|
||||
scoreType = scoreType.ToLowerInvariant();
|
||||
|
||||
number = number.Clamp(1, 100);
|
||||
var exactStatus = status.EndsWith("only");
|
||||
if (exactStatus)
|
||||
status = status[..^4];
|
||||
if (!Enum.TryParse(status, true, out CompatStatus s))
|
||||
s = CompatStatus.Playable;
|
||||
|
||||
await using var db = new ThumbnailDb();
|
||||
var queryBase = db.Thumbnail.AsNoTracking();
|
||||
if (exactStatus)
|
||||
queryBase = queryBase.Where(g => g.CompatibilityStatus == s);
|
||||
else
|
||||
queryBase = queryBase.Where(g => g.CompatibilityStatus >= s);
|
||||
queryBase = queryBase.Where(g => g.Metacritic != null).Include(t => t.Metacritic);
|
||||
var query = scoreType switch
|
||||
{
|
||||
"critic" => queryBase.Where(t => t.Metacritic!.CriticScore > 0).AsEnumerable().Select(t => (title: t.Metacritic!.Title, score: t.Metacritic!.CriticScore!.Value, second: t.Metacritic.UserScore ?? t.Metacritic.CriticScore.Value)),
|
||||
"user" => queryBase.Where(t => t.Metacritic!.UserScore > 0).AsEnumerable().Select(t => (title: t.Metacritic!.Title, score: t.Metacritic!.UserScore!.Value, second: t.Metacritic.CriticScore ?? t.Metacritic.UserScore.Value)),
|
||||
_ => queryBase.AsEnumerable().Select(t => (title: t.Metacritic!.Title, score: Math.Max(t.Metacritic.CriticScore ?? 0, t.Metacritic.UserScore ?? 0), second: (byte)0)),
|
||||
};
|
||||
var resultList = query.Where(i => i.score > 0)
|
||||
.OrderByDescending(i => i.score)
|
||||
.ThenByDescending(i => i.second)
|
||||
.Distinct()
|
||||
.Take(number)
|
||||
.ToList();
|
||||
if (resultList.Count > 0)
|
||||
{
|
||||
var result = new StringBuilder($"Best {s.ToString().ToLower()}");
|
||||
if (exactStatus)
|
||||
result.Append(" only");
|
||||
result.Append(" games");
|
||||
if (scoreType is "critic" or "user")
|
||||
result.Append($" according to {scoreType}s");
|
||||
result.AppendLine(":");
|
||||
foreach (var (title, score, _) in resultList)
|
||||
result.AppendLine($"`{score:00}` {title}");
|
||||
await ctx.SendAutosplitMessageAsync(result, blockStart: null, blockEnd: null).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await ctx.Channel.SendMessageAsync("Failed to generate list").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Command("latest"), TriggersTyping]
|
||||
[Description("Provides links to the latest RPCS3 build")]
|
||||
//[Cooldown(1, 30, CooldownBucketType.Channel)]
|
||||
//[Command("latest")]
|
||||
[Description("Links to the latest RPCS3 build")]
|
||||
public sealed class UpdatesCheck
|
||||
{
|
||||
/*
|
||||
[Command("build"), DefaultGroupCommand]
|
||||
public Task Latest(CommandContext ctx) => CheckForRpcs3Updates(ctx.Client, ctx.Channel);
|
||||
public Task Latest(SlashCommandContext ctx) => CheckForRpcs3Updates(ctx.Client, ctx.Channel);
|
||||
|
||||
[Command("since")]
|
||||
[Description("Show additional info about changes since specified update")]
|
||||
public Task Since(CommandContext ctx, [Description("Commit hash of the update, such as `46abe0f31`")] string commit)
|
||||
public Task Since(SlashCommandContext ctx, [Description("Commit hash of the update, such as `46abe0f31`")] string commit)
|
||||
=> CheckForRpcs3Updates(ctx.Client, ctx.Channel, commit);
|
||||
|
||||
[Command("clear"), RequiresBotModRole]
|
||||
[Description("Clears update info cache that is used to post new build announcements")]
|
||||
public Task Clear(CommandContext ctx)
|
||||
public Task Clear(SlashCommandContext ctx)
|
||||
{
|
||||
lastUpdateInfo = null;
|
||||
lastFullBuildNumber = null;
|
||||
@@ -181,14 +111,15 @@ internal sealed partial class CompatList
|
||||
|
||||
[Command("set"), RequiresBotModRole]
|
||||
[Description("Sets update info cache that is used to check if new updates are available")]
|
||||
public Task Set(CommandContext ctx, string lastUpdatePr)
|
||||
public Task Set(SlashCommandContext ctx, string lastUpdatePr)
|
||||
{
|
||||
lastUpdateInfo = lastUpdatePr;
|
||||
lastFullBuildNumber = null;
|
||||
return CheckForRpcs3Updates(ctx.Client, null);
|
||||
}
|
||||
*/
|
||||
|
||||
public static async Task<bool> CheckForRpcs3Updates(DiscordClient discordClient, DiscordChannel? channel, string? sinceCommit = null, DiscordMessage? emptyBotMsg = null)
|
||||
public static async ValueTask<bool> CheckForRpcs3Updates(DiscordClient discordClient, DiscordChannel? channel, string? sinceCommit = null, DiscordMessage? emptyBotMsg = null)
|
||||
{
|
||||
var updateAnnouncement = channel is null;
|
||||
var updateAnnouncementRestore = emptyBotMsg != null;
|
||||
@@ -271,7 +202,7 @@ internal sealed partial class CompatList
|
||||
if (embed.Color.Value.Value == Config.Colors.Maintenance.Value)
|
||||
return false;
|
||||
|
||||
await CheckMissedBuildsBetween(discordClient, compatChannel, lastUpdateInfo, latestUpdatePr, Config.Cts.Token).ConfigureAwait(false);
|
||||
await CheckMissedBuildsBetweenAsync(discordClient, compatChannel, lastUpdateInfo, latestUpdatePr, Config.Cts.Token).ConfigureAwait(false);
|
||||
|
||||
await compatChannel.SendMessageAsync(embed: embed.Build()).ConfigureAwait(false);
|
||||
lastUpdateInfo = latestUpdatePr;
|
||||
@@ -302,7 +233,7 @@ internal sealed partial class CompatList
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task CheckMissedBuildsBetween(DiscordClient discordClient, DiscordChannel compatChannel, string? previousUpdatePr, string? latestUpdatePr, CancellationToken cancellationToken)
|
||||
private static async ValueTask CheckMissedBuildsBetweenAsync(DiscordClient discordClient, DiscordChannel compatChannel, string? previousUpdatePr, string? latestUpdatePr, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!int.TryParse(previousUpdatePr, out var oldestPr)
|
||||
|| !int.TryParse(latestUpdatePr, out var newestPr))
|
||||
@@ -394,8 +325,7 @@ internal sealed partial class CompatList
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private static async Task DoRequestAndRespond(CommandContext ctx, RequestBuilder requestBuilder)
|
||||
private static async ValueTask DoRequestAndRespondAsync(SlashCommandContext ctx, bool ephemeral, RequestBuilder requestBuilder)
|
||||
{
|
||||
Config.Log.Info(requestBuilder.Build());
|
||||
CompatResult? result = null;
|
||||
@@ -411,7 +341,7 @@ internal sealed partial class CompatList
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
await ctx.Channel.SendMessageAsync(embed: TitleInfo.CommunicationError.AsEmbed(null)).ConfigureAwait(false);
|
||||
await ctx.RespondAsync(embed: TitleInfo.CommunicationError.AsEmbed(null), ephemeral).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -419,14 +349,20 @@ internal sealed partial class CompatList
|
||||
#if DEBUG
|
||||
await Task.Delay(5_000).ConfigureAwait(false);
|
||||
#endif
|
||||
var channel = await ctx.GetChannelForSpamAsync().ConfigureAwait(false);
|
||||
if (result?.Results?.Count == 1)
|
||||
await ProductCodeLookup.LookupAndPostProductCodeEmbedAsync(ctx.Client, ctx.Message, ctx.Channel, [..result.Results.Keys]).ConfigureAwait(false);
|
||||
else if (result != null)
|
||||
foreach (var msg in FormatSearchResults(ctx, result))
|
||||
await channel.SendAutosplitMessageAsync(msg, blockStart: "", blockEnd: "").ConfigureAwait(false);
|
||||
{
|
||||
var formattedResults = await ProductCodeLookup.LookupProductCodeAndFormatAsync(ctx.Client, [..result.Results.Keys]).ConfigureAwait(false);
|
||||
await ctx.RespondAsync(embed: formattedResults[0].builder, ephemeral: ephemeral).ConfigureAwait(false);
|
||||
}
|
||||
else if (result != null)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var msg in FormatSearchResults(ctx, result))
|
||||
builder.AppendLine(msg);
|
||||
var formattedResults = AutosplitResponseHelper.AutosplitMessage(builder.ToString(), blockStart: null, blockEnd: null);
|
||||
await ctx.RespondAsync(formattedResults[0], ephemeral).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
internal static CompatResult GetLocalCompatResult(RequestBuilder requestBuilder)
|
||||
{
|
||||
@@ -457,38 +393,29 @@ internal sealed partial class CompatList
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
private static IEnumerable<string> FormatSearchResults(CommandContext ctx, CompatResult compatResult)
|
||||
private static IEnumerable<string> FormatSearchResults(SlashCommandContext ctx, CompatResult compatResult)
|
||||
{
|
||||
var returnCode = ApiConfig.ReturnCodes[compatResult.ReturnCode];
|
||||
var request = compatResult.RequestBuilder;
|
||||
|
||||
if (returnCode.overrideAll)
|
||||
yield return string.Format(returnCode.info, ctx.Message.Author.Mention);
|
||||
yield return string.Format(returnCode.info, ctx.User.Mention);
|
||||
else
|
||||
{
|
||||
var authorMention = ctx.Channel.IsPrivate ? "You" : ctx.Message.Author.Mention;
|
||||
var authorMention = ctx.Channel.IsPrivate ? "You" : ctx.User.Mention;
|
||||
var result = new StringBuilder();
|
||||
result.AppendLine($"{authorMention} searched for: ***{request.Search?.Sanitize(replaceBackTicks: true)}***");
|
||||
if (request.Search?.Contains("persona", StringComparison.InvariantCultureIgnoreCase) is true
|
||||
|| request.Search?.Contains("p5", StringComparison.InvariantCultureIgnoreCase) is true)
|
||||
result.AppendLine("Did you try searching for **__Unnamed__** instead?");
|
||||
else if (ctx.IsOnionLike()
|
||||
&& compatResult.Results.Values.Any(i =>
|
||||
i.Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| i.Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase))
|
||||
)
|
||||
{
|
||||
var sqvat = ctx.Client.GetEmoji(":sqvat:", Config.Reactions.No);
|
||||
result.AppendLine($"One day this meme will die {sqvat}");
|
||||
}
|
||||
result.AppendFormat(returnCode.info, compatResult.SearchTerm);
|
||||
yield return result.ToString();
|
||||
|
||||
result.Clear();
|
||||
|
||||
if (returnCode.displayResults)
|
||||
{
|
||||
if (!returnCode.displayResults)
|
||||
yield break;
|
||||
|
||||
var sortedList = compatResult.GetSortedList();
|
||||
var trimmedList = sortedList.Where(i => i.score > 0).ToList();
|
||||
if (trimmedList.Count > 0)
|
||||
@@ -511,8 +438,6 @@ internal sealed partial class CompatList
|
||||
yield return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public static string FixGameTitleSearch(string title)
|
||||
{
|
||||
@@ -570,7 +495,7 @@ internal sealed partial class CompatList
|
||||
await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task ImportMetacriticScoresAsync()
|
||||
public static async ValueTask ImportMetacriticScoresAsync()
|
||||
{
|
||||
var scoreJson = "metacritic_ps3.json";
|
||||
string json;
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace CompatBot.EventHandlers;
|
||||
|
||||
internal static class GlobalButtonHandler
|
||||
{
|
||||
private const string ReplaceWithUpdatesPrefix = "replace with game updates:";
|
||||
internal const string ReplaceWithUpdatesPrefix = "replace with game updates:";
|
||||
|
||||
public static async Task OnComponentInteraction(DiscordClient sender, ComponentInteractionCreatedEventArgs e)
|
||||
{
|
||||
|
||||
@@ -47,34 +47,20 @@ internal static partial class ProductCodeLookup
|
||||
await LookupAndPostProductCodeEmbedAsync(c, args.Message, args.Channel, codesToLookup).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task LookupAndPostProductCodeEmbedAsync(DiscordClient client, DiscordMessage message, DiscordChannel channel, List<string> codesToLookup)
|
||||
public static async ValueTask LookupAndPostProductCodeEmbedAsync(DiscordClient client, DiscordMessage message, DiscordChannel channel, List<string> codesToLookup)
|
||||
{
|
||||
await message.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var results = new List<(string code, Task<DiscordEmbedBuilder> task)>(codesToLookup.Count);
|
||||
foreach (var code in codesToLookup)
|
||||
results.Add((code, client.LookupGameInfoAsync(code)));
|
||||
var formattedResults = new List<(string code, DiscordEmbedBuilder builder)>(results.Count);
|
||||
foreach (var (code, task) in results)
|
||||
try
|
||||
{
|
||||
formattedResults.Add((code, await task.ConfigureAwait(false)));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Config.Log.Warn(e, $"Couldn't get product code info for {code}");
|
||||
}
|
||||
|
||||
// get only results with unique titles
|
||||
formattedResults = formattedResults.DistinctBy(e => e.builder.Title).ToList();
|
||||
var lookupEmoji = new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🔍"));
|
||||
var formattedResults = await LookupProductCodeAndFormatAsync(client, codesToLookup).ConfigureAwait(false);
|
||||
foreach (var result in formattedResults)
|
||||
try
|
||||
{
|
||||
var messageBuilder = new DiscordMessageBuilder().AddEmbed(result.builder);
|
||||
if (channel.IsSpamChannel())
|
||||
messageBuilder.AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Secondary, $"replace with game updates:{message.Author.Id}:{message.Id}:{result.code}", "Check for updates", emoji: lookupEmoji));
|
||||
//todo: pass author from context and update OnCheckUpdatesButtonClick in psn check updates
|
||||
if (message is {Author: not null} && channel.IsSpamChannel())
|
||||
messageBuilder.AddComponents(new DiscordButtonComponent(DiscordButtonStyle.Secondary, $"{GlobalButtonHandler.ReplaceWithUpdatesPrefix}{message.Author.Id}:{message.Id}:{result.code}", "Check for updates", emoji: lookupEmoji));
|
||||
await DiscordMessageExtensions.UpdateOrCreateMessageAsync(null, channel, messageBuilder).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -88,10 +74,27 @@ internal static partial class ProductCodeLookup
|
||||
}
|
||||
}
|
||||
|
||||
internal static async ValueTask<List<(string code, DiscordEmbedBuilder builder)>> LookupProductCodeAndFormatAsync(DiscordClient client, List<string> codesToLookup)
|
||||
{
|
||||
var results = codesToLookup.Select(code => (code, client.LookupGameInfoAsync(code))).ToList();
|
||||
var formattedResults = new List<(string code, DiscordEmbedBuilder builder)>(results.Count);
|
||||
foreach (var (code, task) in results)
|
||||
try
|
||||
{
|
||||
formattedResults.Add((code, await task.ConfigureAwait(false)));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Config.Log.Warn(e, $"Couldn't get product code info for {code}");
|
||||
}
|
||||
// get only results with unique titles
|
||||
return formattedResults.DistinctBy(e => e.builder.Title).ToList();
|
||||
}
|
||||
|
||||
public static List<string> GetProductIds(string? input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return new(0);
|
||||
return [];
|
||||
|
||||
return Pattern().Matches(input)
|
||||
.Select(match => (match.Groups["letters"].Value + match.Groups["numbers"]).ToUpper())
|
||||
@@ -102,7 +105,7 @@ internal static partial class ProductCodeLookup
|
||||
public static async Task<DiscordEmbedBuilder> LookupGameInfoAsync(this DiscordClient client, string? code, string? gameTitle = null, bool forLog = false, string? category = null)
|
||||
=> (await LookupGameInfoWithEmbedAsync(client, code, gameTitle, forLog, category).ConfigureAwait(false)).embedBuilder;
|
||||
|
||||
public static async Task<(DiscordEmbedBuilder embedBuilder, CompatResult? compatResult)> LookupGameInfoWithEmbedAsync(this DiscordClient client, string? code, string? gameTitle = null, bool forLog = false, string? category = null)
|
||||
public static async ValueTask<(DiscordEmbedBuilder embedBuilder, CompatResult? compatResult)> LookupGameInfoWithEmbedAsync(this DiscordClient client, string? code, string? gameTitle = null, bool forLog = false, string? category = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(code))
|
||||
return (TitleInfo.Unknown.AsEmbed(code, gameTitle, forLog), null);
|
||||
|
||||
@@ -7,13 +7,13 @@ public static class AutosplitResponseHelper
|
||||
public static Task SendAutosplitMessageAsync(this CommandContext ctx, StringBuilder message, int blockSize = EmbedPager.MaxMessageLength, string? blockEnd = "\n```", string? blockStart = "```\n")
|
||||
=> ctx.Channel.SendAutosplitMessageAsync(message, blockSize, blockEnd, blockStart);
|
||||
|
||||
public static Task SendAutosplitMessageAsync(this CommandContext ctx, string message, int blockSize = EmbedPager.MaxMessageLength, string? blockEnd = "\n```", string? blockStart = "```\n")
|
||||
public static ValueTask SendAutosplitMessageAsync(this CommandContext ctx, string message, int blockSize = EmbedPager.MaxMessageLength, string? blockEnd = "\n```", string? blockStart = "```\n")
|
||||
=> ctx.Channel.SendAutosplitMessageAsync(message, blockSize, blockEnd, blockStart);
|
||||
|
||||
public static async Task SendAutosplitMessageAsync(this DiscordChannel channel, StringBuilder message, int blockSize = EmbedPager.MaxMessageLength, string? blockEnd = "\n```", string? blockStart = "```\n")
|
||||
=> await SendAutosplitMessageAsync(channel, message.ToString(), blockSize, blockEnd, blockStart).ConfigureAwait(false);
|
||||
|
||||
public static async Task SendAutosplitMessageAsync(this DiscordChannel channel, string message, int blockSize = EmbedPager.MaxMessageLength, string? blockEnd = "\n```", string? blockStart = "```\n")
|
||||
public static async ValueTask SendAutosplitMessageAsync(this DiscordChannel channel, string message, int blockSize = EmbedPager.MaxMessageLength, string? blockEnd = "\n```", string? blockStart = "```\n")
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
@@ -55,4 +55,33 @@ public static class AutosplitResponseHelper
|
||||
Config.Log.Warn(e, "And yet the message length was " + remainingContent.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<string> AutosplitMessage(string message, int blockSize = EmbedPager.MaxMessageLength, string? blockEnd = "\n```", string? blockStart = "```\n")
|
||||
{
|
||||
var result = new List<string>();
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return [];
|
||||
|
||||
blockEnd ??= "";
|
||||
blockStart ??= "";
|
||||
var maxContentSize = blockSize - blockEnd.Length - blockStart.Length;
|
||||
var buffer = new StringBuilder();
|
||||
foreach (var line in message.Split(Environment.NewLine).Select(l => l.Trim(maxContentSize)))
|
||||
{
|
||||
if (buffer.Length + line.Length + blockEnd.Length > blockSize)
|
||||
{
|
||||
var content = buffer.ToString().Trim(blockSize - blockEnd.Length) + blockEnd;
|
||||
if (content.Length > blockSize)
|
||||
Config.Log.Error($"Somehow managed to go over {blockSize} characters in a message");
|
||||
result.Add(content);
|
||||
buffer.Clear().Append(blockStart);
|
||||
}
|
||||
else
|
||||
buffer.Append('\n');
|
||||
buffer.Append(line);
|
||||
}
|
||||
var remainingContent = buffer.ToString().Trim(blockSize);
|
||||
result.Add(remainingContent);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
5
ExternalAnnotations/DSharpPlus.Commands.xml
Normal file
5
ExternalAnnotations/DSharpPlus.Commands.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<assembly name="DSharpPlus.Commands">
|
||||
<member name="T:DSharpPlus.Commands.CommandAttribute">
|
||||
<attribute ctor="M:JetBrains.Annotations.MeansImplicitUseAttribute.#ctor"/>
|
||||
</member>
|
||||
</assembly>
|
||||
Reference in New Issue
Block a user