using BirthdayBot.Data;
using NodaTime;
using System.Collections.ObjectModel;
namespace BirthdayBot.ApplicationCommands;
///
/// Base class for classes handling slash command execution.
///
internal abstract class BotApplicationCommand {
public delegate Task CommandResponder(ShardInstance instance, GuildConfiguration gconf, SocketSlashCommand arg);
protected const string ErrGuildOnly = ":x: This command can only be run within a server.";
protected string ErrNotAllowed = ":x: Only server moderators may use this command.";
protected const string MemberCacheEmptyError = ":warning: Please try the command again.";
public const string AccessDeniedError = ":warning: You are not allowed to run this command.";
protected static ReadOnlyDictionary TzNameMap { get; }
///
/// Returns a list of application command definitions handled by the implementing class,
/// for use when registering/updating this bot's available slash commands.
///
public abstract IEnumerable GetCommands();
///
/// Given the command name, returns the designated handler to execute to fulfill the command.
/// Returns null if this class does not contain a handler for the given command.
///
public abstract CommandResponder? GetHandlerFor(string commandName);
static BotApplicationCommand() {
var dict = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var name in DateTimeZoneProviders.Tzdb.Ids) dict.Add(name, name);
TzNameMap = new(dict);
}
///
/// Checks given time zone input. Returns a valid string for use with NodaTime,
/// throwing a FormatException if the input is not recognized.
///
protected static string ParseTimeZone(string tzinput) {
if (!TzNameMap.TryGetValue(tzinput, out string? tz)) throw new FormatException(":x: Unexpected time zone name."
+ $" Refer to `INSERT COMMAND NAME HERE` to help determine the correct value."); // TODO fix!!!!!!!!!!!!!!!!!!!
return tz!;
}
///
/// An alternative to to be called by command handlers needing a full member cache.
/// Creates a download request if necessary.
///
///
/// True if the member cache is already filled, false otherwise.
///
///
/// Any updates to the member cache aren't accessible until the event handler finishes execution, meaning proactive downloading
/// is necessary, and is handled by . In situations where
/// this approach fails, this is to be called, and the user must be asked to attempt the command again if this returns false.
///
protected static async Task HasMemberCacheAsync(SocketGuild guild) {
if (Common.HasMostMembersDownloaded(guild)) return true;
// Event handling thread hangs if awaited normally or used with Task.Run
await Task.Factory.StartNew(guild.DownloadUsersAsync).ConfigureAwait(false);
return false;
}
}