ProjectMakoto.Plugins.Score.../Commands/ScoreSaberSearchCommand.cs
2025-01-27 18:36:44 +01:00

313 lines
No EOL
17 KiB
C#

// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
using System.Globalization;
using ProjectMakoto.Entities.ScoreSaber;
using ProjectMakoto.Exceptions;
using ProjectMakoto.Plugins.ScoreSaber;
namespace ProjectMakoto.Commands;
internal sealed class ScoreSaberSearchCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var CommandKey = ((Plugins.ScoreSaber.Entities.Translations)ScoreSaberPlugin.Plugin!.Translations).Commands.ScoreSaber;
var name = (string)arguments["name"];
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
DiscordStringSelectComponent GetContinents(string default_code)
{
List<DiscordStringSelectComponentOption> continents = new() { new DiscordStringSelectComponentOption(this.GetString(CommandKey.Search.NoCountryFilter), "no_country", "", (default_code == "no_country")) };
foreach (var b in ctx.Bot.CountryCodes.List.GroupBy(x => x.Value.ContinentCode).Select(x => x.First()).Take(24))
{
continents.Add(new DiscordStringSelectComponentOption($"{b.Value.ContinentName}", b.Value.ContinentCode, "", (default_code == b.Value.ContinentCode)));
}
return new DiscordStringSelectComponent(this.GetString(CommandKey.Search.SelectContinentDropdown), continents as IEnumerable<DiscordStringSelectComponentOption>, "continent_selection");
}
DiscordStringSelectComponent GetCountries(string continent_code, string default_country, int page)
{
List<DiscordStringSelectComponentOption> countries = new();
var currentCountryList = ctx.Bot.CountryCodes.List.Where(x => x.Value.ContinentCode.ToLower() == continent_code.ToLower()).Skip((page - 1) * 25).Take(25).ToList();
foreach (var b in currentCountryList)
{
DiscordEmoji flag_emote = null;
try
{ flag_emote = DiscordEmoji.FromName(ctx.Client, $":flag_{b.Key.ToLower()}:"); }
catch (Exception) { flag_emote = DiscordEmoji.FromUnicode("⬜"); }
countries.Add(new DiscordStringSelectComponentOption($"{b.Value.Name}", b.Key, "", (b.Key == default_country), new DiscordComponentEmoji(flag_emote)));
}
return new DiscordStringSelectComponent(this.GetString(CommandKey.Search.SelectCountryDropdown), countries as IEnumerable<DiscordStringSelectComponentOption>, "country_selection");
}
var start_search_button = new DiscordButtonComponent(ButtonStyle.Success, "start_search", this.GetString(CommandKey.Search.StartSearch), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🔎")));
var next_step_button = new DiscordButtonComponent(ButtonStyle.Primary, "next_step", this.GetString(CommandKey.Search.NextStep), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("▶")));
var previous_page_button = new DiscordButtonComponent(ButtonStyle.Primary, "prev_page", this.GetString(this.t.Common.PreviousPage), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("◀")));
var next_page_button = new DiscordButtonComponent(ButtonStyle.Primary, "next_page", this.GetString(this.t.Common.NextPage), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("▶")));
var embed = new DiscordEmbedBuilder
{
Description = this.GetString(CommandKey.Search.SelectContinent, true)
}.AsAwaitingInput(ctx, "Score Saber");
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed).AddComponents(GetContinents("no_country")).AddComponents(start_search_button));
CancellationTokenSource tokenSource = new();
var selectedContinent = "no_country";
var selectedCountry = "no_country";
var lastFetchedPage = -1;
var currentPage = 1;
var currentFetchedPage = 1;
var playerSelection = false;
PlayerSearch lastSearch = null;
async Task RunDropdownInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
{
_ = Task.Run(async () =>
{
try
{
if (e.Message?.Id == ctx.ResponseMessage.Id && e.User.Id == ctx.User.Id)
{
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
async Task RefreshCountryList()
{
embed.Description = this.GetString(CommandKey.Search.SelectCountry, true);
if (selectedCountry != "no_country")
{
embed.Description += $"\n`{this.GetString(CommandKey.Search.SelectedCountry)}: '{ctx.Bot.CountryCodes.List[selectedCountry].Name}'`";
}
var page = GetCountries(selectedContinent, selectedCountry, currentPage);
var builder = new DiscordMessageBuilder().WithEmbed(embed).AddComponents(page);
if (currentPage == 1 && ctx.Bot.CountryCodes.List.Where(x => x.Value.ContinentCode.ToLower() == selectedContinent.ToLower()).Count() > 25)
{
_ = builder.AddComponents(next_page_button);
}
if (currentPage != 1)
{
if (ctx.Bot.CountryCodes.List.Where(x => x.Value.ContinentCode.ToLower() == selectedContinent.ToLower()).Skip((currentPage - 1) * 25).Count() > 25)
_ = builder.AddComponents(next_page_button);
_ = builder.AddComponents(previous_page_button);
}
if (selectedCountry != "no_country")
_ = builder.AddComponents(start_search_button);
_ = await this.RespondOrEdit(builder);
}
async Task RefreshPlayerList()
{
ctx.Client.ComponentInteractionCreated -= RunDropdownInteraction;
embed.Description = this.GetString(CommandKey.Search.Searching, true);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed.AsLoading(ctx, "Score Saber")));
if (currentFetchedPage != lastFetchedPage)
{
try
{
lastSearch = await ScoreSaberPlugin.ScoreSaber!.SearchPlayer(name, currentFetchedPage, (selectedCountry != "no_country" ? selectedCountry : ""));
}
catch (InternalServerErrorException)
{
tokenSource.Cancel();
ctx.Client.ComponentInteractionCreated -= RunDropdownInteraction;
embed.Description = this.GetString(CommandKey.InternalServerError, true);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed.AsError(ctx, "Score Saber")));
return;
}
catch (ForbiddenException)
{
tokenSource.Cancel();
ctx.Client.ComponentInteractionCreated -= RunDropdownInteraction;
embed.Description = this.GetString(CommandKey.ForbiddenError, true);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed.AsError(ctx, "Score Saber")));
return;
}
catch (Exception)
{
throw;
}
lastFetchedPage = currentFetchedPage;
}
List<DiscordStringSelectComponentOption> playerDropDownOptions = new();
var playerList = lastSearch.players.Skip((currentPage - 1) * 25).Take(25).ToList();
foreach (var b in playerList)
{
playerDropDownOptions.Add(new DiscordStringSelectComponentOption($"{b.name.FullSanitize()} | {b.pp.ToString("N2", CultureInfo.CreateSpecificCulture("en-US"))}pp", b.id, $"🌐 #{b.rank} | {b.country.IsoCountryCodeToFlagEmoji()} #{b.countryRank}"));
}
var player_dropdown = new DiscordStringSelectComponent(this.GetString(CommandKey.Search.SelectPlayer), playerDropDownOptions as IEnumerable<DiscordStringSelectComponentOption>, "player_selection");
var builder = new DiscordMessageBuilder().AddComponents(player_dropdown);
var added_next = false;
if (currentPage == 1 && lastSearch.players.Length > 25)
{
_ = builder.AddComponents(next_page_button);
added_next = true;
}
if (currentPage != 1 || lastFetchedPage != 1)
{
if ((lastSearch.players.Skip((currentPage - 1) * 25).Take(25).Count() > 25 || ((((lastSearch.metadata.total - (currentFetchedPage - 1)) * 50) > 0) && player_dropdown.Options.Count == 25)) && !added_next)
_ = builder.AddComponents(next_page_button);
_ = builder.AddComponents(previous_page_button);
}
ctx.Client.ComponentInteractionCreated += RunDropdownInteraction;
embed.Description = this.GetString(CommandKey.Search.FoundCount, true, new TVar("TotalCount", lastSearch.metadata.total));
_ = await this.RespondOrEdit(builder.WithEmbed(embed.AsSuccess(ctx, "Score Saber")));
}
if (e.GetCustomId() == "start_search")
{
tokenSource.Cancel();
tokenSource = null;
playerSelection = true;
currentPage = 1;
await RefreshPlayerList();
tokenSource = new();
}
else if (e.GetCustomId() == "player_selection")
{
ctx.Client.ComponentInteractionCreated -= RunDropdownInteraction;
tokenSource.Cancel();
tokenSource = null;
await ScoreSaberCommandAbstractions.SendScoreSaberProfile(ctx, this, e.Values.First());
return;
}
else if (e.GetCustomId() == "next_step")
{
_ = RefreshCountryList();
}
else if (e.GetCustomId() == "country_selection")
{
selectedCountry = e.Values.First();
_ = RefreshCountryList();
}
else if (e.GetCustomId() == "prev_page")
{
if (playerSelection)
{
if (currentPage == 1)
{
currentPage = 2;
currentFetchedPage -= 1;
}
else
currentPage -= 1;
tokenSource.Cancel();
tokenSource = null;
await RefreshPlayerList();
tokenSource = new();
}
else
{
currentPage -= 1;
_ = RefreshCountryList();
}
}
else if (e.GetCustomId() == "next_page")
{
if (playerSelection)
{
if (currentPage == 2)
{
currentPage = 1;
currentFetchedPage += 1;
}
else
currentPage += 1;
tokenSource.Cancel();
tokenSource = null;
await RefreshPlayerList();
tokenSource = new();
}
else
{
currentPage += 1;
_ = RefreshCountryList();
}
}
else if (e.GetCustomId() == "continent_selection")
{
selectedContinent = e.Values.First();
_ = selectedContinent != "no_country"
? await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed).AddComponents(GetContinents(selectedContinent)).AddComponents(next_step_button))
: await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed).AddComponents(GetContinents(selectedContinent)).AddComponents(start_search_button));
}
try
{
tokenSource.Cancel();
tokenSource = new();
await Task.Delay(120000, tokenSource.Token);
this.ModifyToTimedOut();
ctx.Client.ComponentInteractionCreated -= RunDropdownInteraction;
}
catch { }
}
}
catch (NotFoundException)
{
embed.Description = this.GetString(CommandKey.Search.NoSearchResult, true);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed.AsError(ctx, "Score Saber")));
}
catch (Exception)
{
ctx.Client.ComponentInteractionCreated -= RunDropdownInteraction;
throw;
}
}).Add(ctx.Bot, ctx);
}
ctx.Client.ComponentInteractionCreated += RunDropdownInteraction;
try
{
await Task.Delay(120000, tokenSource.Token);
this.ModifyToTimedOut();
ctx.Client.ComponentInteractionCreated -= RunDropdownInteraction;
}
catch { }
});
}
}