1720 lines
84 KiB
C#
1720 lines
84 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 DisCatSharp.Extensions.TwoFactorCommands.Enums;
|
||
|
||
namespace ProjectMakoto.Commands;
|
||
|
||
public abstract class BaseCommand
|
||
{
|
||
public SharedCommandContext ctx { private get; set; }
|
||
public Translations t { get; set; }
|
||
|
||
#region Execution
|
||
public virtual async Task<bool> BeforeExecution(SharedCommandContext ctx)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
public abstract Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments = null);
|
||
|
||
public async Task TransferCommand(SharedCommandContext ctx, Dictionary<string, object> arguments = null)
|
||
{
|
||
this.t = ctx.Bot.LoadedTranslations;
|
||
this.ctx = ctx;
|
||
|
||
ctx.Transferred = true;
|
||
|
||
if (await this.BasePreExecutionCheck())
|
||
await this.ExecuteCommand(this.ctx, arguments).Add(ctx.Bot, this.ctx);
|
||
}
|
||
|
||
public async Task ExecuteCommand(CommandContext ctx, Bot _bot, Dictionary<string, object> arguments = null)
|
||
{
|
||
this.ctx = new SharedCommandContext(this, ctx, _bot);
|
||
this.t = _bot.LoadedTranslations;
|
||
|
||
if (await this.BasePreExecutionCheck())
|
||
await this.ExecuteCommand(this.ctx, arguments).Add(_bot, this.ctx);
|
||
}
|
||
|
||
public async Task ExecuteCommand(InteractionContext ctx, Bot _bot, Dictionary<string, object> arguments = null, bool Ephemeral = true, bool InitiateInteraction = true, bool InteractionInitiated = false)
|
||
{
|
||
this.ctx = new SharedCommandContext(this, ctx, _bot);
|
||
this.t = _bot.LoadedTranslations;
|
||
|
||
await Task.Run(async () =>
|
||
{
|
||
if (InitiateInteraction)
|
||
await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
||
{
|
||
IsEphemeral = Ephemeral
|
||
});
|
||
|
||
this.ctx.RespondedToInitial = InitiateInteraction;
|
||
|
||
if (InteractionInitiated)
|
||
this.ctx.RespondedToInitial = true;
|
||
|
||
if (await this.BasePreExecutionCheck())
|
||
await this.ExecuteCommand(this.ctx, arguments).Add(_bot, this.ctx);
|
||
}).Add(_bot, this.ctx);
|
||
}
|
||
|
||
public async Task ExecuteCommandWith2FA(InteractionContext ctx, Bot _bot, Dictionary<string, object> arguments = null)
|
||
{
|
||
this.ctx = new SharedCommandContext(this, ctx, _bot);
|
||
this.t = _bot.LoadedTranslations;
|
||
|
||
await Task.Run(async () =>
|
||
{
|
||
this.ctx.RespondedToInitial = false;
|
||
|
||
if (!this.ctx.Bot.status.LoadedConfig.IsDev)
|
||
if (!ctx.Client.CheckTwoFactorEnrollmentFor(ctx.User.Id))
|
||
{
|
||
_ = ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(new DiscordEmbedBuilder()
|
||
{
|
||
Description = "`Please enroll in Two Factor Authentication via 'Enroll2FA'.`"
|
||
}.AsError(this.ctx)).AsEphemeral());
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
if (_bot.Users[ctx.User.Id].LastSuccessful2FA.GetTimespanSince() > TimeSpan.FromMinutes(3))
|
||
{
|
||
this.ctx.RespondedToInitial = true;
|
||
var tfa = await ctx.RequestTwoFactorAsync();
|
||
|
||
if (tfa.Result is TwoFactorResult.ValidCode or TwoFactorResult.InvalidCode)
|
||
await this.SwitchToEvent(tfa.ComponentInteraction);
|
||
|
||
if (tfa.Result != TwoFactorResult.ValidCode)
|
||
{
|
||
_ = this.RespondOrEdit(new DiscordMessageBuilder().WithContent("Invalid Code."));
|
||
return;
|
||
}
|
||
_bot.Users[ctx.User.Id].LastSuccessful2FA = DateTime.UtcNow;
|
||
}
|
||
}
|
||
|
||
if (!this.ctx.RespondedToInitial)
|
||
await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
||
{
|
||
IsEphemeral = true
|
||
});
|
||
|
||
if (await this.BasePreExecutionCheck())
|
||
await this.ExecuteCommand(this.ctx, arguments).Add(_bot, this.ctx);
|
||
}).Add(_bot, this.ctx);
|
||
}
|
||
|
||
public async Task ExecuteCommand(ContextMenuContext ctx, Bot _bot, Dictionary<string, object> arguments = null, bool Ephemeral = true, bool InitiateInteraction = true, bool InteractionInitiated = false)
|
||
{
|
||
this.ctx = new SharedCommandContext(this, ctx, _bot);
|
||
this.t = _bot.LoadedTranslations;
|
||
|
||
await Task.Run(async () =>
|
||
{
|
||
if (InitiateInteraction)
|
||
await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
||
{
|
||
IsEphemeral = Ephemeral
|
||
});
|
||
|
||
this.ctx.RespondedToInitial = InitiateInteraction;
|
||
|
||
if (InteractionInitiated)
|
||
this.ctx.RespondedToInitial = true;
|
||
|
||
if (await this.BasePreExecutionCheck())
|
||
await this.ExecuteCommand(this.ctx, arguments).Add(_bot, this.ctx);
|
||
}).Add(_bot, this.ctx);
|
||
}
|
||
|
||
public async Task ExecuteCommand(ComponentInteractionCreateEventArgs ctx, DiscordClient client, string commandName, Bot _bot, Dictionary<string, object> arguments = null, bool Ephemeral = true, bool InitiateInteraction = true, bool InteractionInitiated = false)
|
||
{
|
||
this.ctx = new SharedCommandContext(this, ctx, client, commandName, _bot);
|
||
this.t = _bot.LoadedTranslations;
|
||
|
||
await Task.Run(async () =>
|
||
{
|
||
if (InitiateInteraction)
|
||
await ctx.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
||
{
|
||
IsEphemeral = Ephemeral
|
||
});
|
||
|
||
this.ctx.RespondedToInitial = InitiateInteraction;
|
||
|
||
if (InteractionInitiated)
|
||
this.ctx.RespondedToInitial = true;
|
||
|
||
if (await this.BasePreExecutionCheck())
|
||
await this.ExecuteCommand(this.ctx, arguments).Add(_bot, this.ctx);
|
||
}).Add(_bot, ctx);
|
||
}
|
||
|
||
private async Task<bool> BasePreExecutionCheck()
|
||
{
|
||
if (this.t is null)
|
||
{
|
||
Log.Warning($"The translation were not set before the BasePreExecutionCheck()!");
|
||
this.t = this.ctx.Bot.LoadedTranslations;
|
||
}
|
||
|
||
if (this.ctx.Bot.Users.ContainsKey(this.ctx.User.Id) && !this.ctx.User.Locale.IsNullOrWhiteSpace() && this.ctx.DbUser.CurrentLocale != this.ctx.User.Locale)
|
||
{
|
||
this.ctx.DbUser.CurrentLocale = this.ctx.User.Locale;
|
||
Log.Debug("Updated language for User '{User}' to '{Locale}'", this.ctx.User.Id, this.ctx.User.Locale);
|
||
}
|
||
|
||
if (this.ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Contains(this.ctx.ParentCommandName))
|
||
{
|
||
this.SendDisabledCommandError(this.ctx.ParentCommandName);
|
||
return false;
|
||
}
|
||
|
||
if (this.ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Contains(this.ctx.CommandName))
|
||
{
|
||
this.SendDisabledCommandError(this.ctx.CommandName);
|
||
return false;
|
||
}
|
||
|
||
if (!this.ctx.Channel.IsPrivate)
|
||
{
|
||
if (this.ctx.Bot.Guilds.ContainsKey(this.ctx.Guild.Id) && !this.ctx.Guild.PreferredLocale.IsNullOrWhiteSpace() && this.ctx.Bot.Guilds[this.ctx.Guild.Id].CurrentLocale != this.ctx.Guild.PreferredLocale)
|
||
{
|
||
this.ctx.Bot.Guilds[this.ctx.Guild.Id].CurrentLocale = this.ctx.Guild.PreferredLocale;
|
||
Log.Debug("Updated language for Guild '{Guild}' to '{Locale}'", this.ctx.Guild.Id, this.ctx.Guild.PreferredLocale);
|
||
}
|
||
|
||
if (!(await this.CheckOwnPermissions(Permissions.SendMessages)))
|
||
return false;
|
||
|
||
if (!(await this.CheckOwnPermissions(Permissions.EmbedLinks)))
|
||
return false;
|
||
|
||
if (!(await this.CheckOwnPermissions(Permissions.AddReactions)))
|
||
return false;
|
||
|
||
if (!(await this.CheckOwnPermissions(Permissions.AccessChannels)))
|
||
return false;
|
||
|
||
if (!(await this.CheckOwnPermissions(Permissions.AttachFiles)))
|
||
return false;
|
||
|
||
if (!(await this.CheckOwnPermissions(Permissions.ManageMessages)))
|
||
return false;
|
||
|
||
if (!(await this.BeforeExecution(this.ctx)))
|
||
return false;
|
||
}
|
||
|
||
if ((this.ctx.Bot.objectedUsers.Contains(this.ctx.User.Id) || this.ctx.DbUser.Data.DeletionRequested) && this.ctx.CommandName != "data" && this.ctx.CommandName != "delete")
|
||
{
|
||
this.SendDataError();
|
||
return false;
|
||
}
|
||
|
||
if (this.ctx.Bot.bannedUsers.TryGetValue(this.ctx.User.Id, out var blacklistedUserDetails))
|
||
{
|
||
this.SendUserBanError(blacklistedUserDetails);
|
||
return false;
|
||
}
|
||
|
||
if (this.ctx.Bot.bannedGuilds.TryGetValue(this.ctx.Guild?.Id ?? 0, out var blacklistedGuildDetails))
|
||
{
|
||
this.SendGuildBanError(blacklistedGuildDetails);
|
||
return false;
|
||
}
|
||
|
||
|
||
return !this.ctx.User.IsBot;
|
||
}
|
||
#endregion
|
||
|
||
public async Task SwitchToEvent(ComponentInteractionCreateEventArgs e)
|
||
{
|
||
await e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
||
{
|
||
IsEphemeral = true
|
||
});
|
||
this.ctx.RespondedToInitial = true;
|
||
this.ctx.OriginalComponentInteractionCreateEventArgs = e;
|
||
this.ctx.CommandType = Enums.CommandType.Event;
|
||
}
|
||
|
||
#region RespondOrEdit
|
||
public Task<DiscordMessage> RespondOrEdit(DiscordEmbed embed)
|
||
=> this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(embed));
|
||
|
||
public Task<DiscordMessage> RespondOrEdit(DiscordEmbedBuilder embed)
|
||
=> this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(embed.Build()));
|
||
|
||
public Task<DiscordMessage> RespondOrEdit(string content)
|
||
=> this.RespondOrEdit(new DiscordMessageBuilder().WithContent(content));
|
||
|
||
public async Task<DiscordMessage> RespondOrEdit(DiscordMessageBuilder discordMessageBuilder)
|
||
{
|
||
switch (this.ctx.CommandType)
|
||
{
|
||
case Enums.CommandType.ApplicationCommand:
|
||
{
|
||
DiscordWebhookBuilder discordWebhookBuilder = new();
|
||
|
||
var files = new Dictionary<string, Stream>();
|
||
|
||
foreach (var b in discordMessageBuilder.Files)
|
||
files.Add(b.Filename, b.Stream);
|
||
|
||
if (discordMessageBuilder.Components.IsNotNullAndNotEmpty())
|
||
_ = discordWebhookBuilder.AddComponents(discordMessageBuilder.Components);
|
||
|
||
_ = discordWebhookBuilder.AddEmbeds(discordMessageBuilder.Embeds);
|
||
_ = discordWebhookBuilder.AddFiles(files);
|
||
discordWebhookBuilder.Content = discordMessageBuilder.Content;
|
||
|
||
var msg = await this.ctx.OriginalInteractionContext.EditResponseAsync(discordWebhookBuilder);
|
||
this.ctx.ResponseMessage = msg;
|
||
return msg;
|
||
}
|
||
|
||
case Enums.CommandType.ContextMenu:
|
||
{
|
||
DiscordWebhookBuilder discordWebhookBuilder = new();
|
||
|
||
var files = new Dictionary<string, Stream>();
|
||
|
||
foreach (var b in discordMessageBuilder.Files)
|
||
files.Add(b.Filename, b.Stream);
|
||
|
||
if (discordMessageBuilder.Components.IsNotNullAndNotEmpty())
|
||
_ = discordWebhookBuilder.AddComponents(discordMessageBuilder.Components);
|
||
|
||
_ = discordWebhookBuilder.AddEmbeds(discordMessageBuilder.Embeds);
|
||
_ = discordWebhookBuilder.AddFiles(files);
|
||
discordWebhookBuilder.Content = discordMessageBuilder.Content;
|
||
|
||
var msg = await this.ctx.OriginalContextMenuContext.EditResponseAsync(discordWebhookBuilder);
|
||
this.ctx.ResponseMessage = msg;
|
||
return msg;
|
||
}
|
||
|
||
case Enums.CommandType.Event:
|
||
{
|
||
DiscordWebhookBuilder discordWebhookBuilder = new();
|
||
|
||
var files = new Dictionary<string, Stream>();
|
||
|
||
foreach (var b in discordMessageBuilder.Files)
|
||
files.Add(b.Filename, b.Stream);
|
||
|
||
if (discordMessageBuilder.Components.IsNotNullAndNotEmpty())
|
||
_ = discordWebhookBuilder.AddComponents(discordMessageBuilder.Components);
|
||
|
||
_ = discordWebhookBuilder.AddEmbeds(discordMessageBuilder.Embeds);
|
||
_ = discordWebhookBuilder.AddFiles(files);
|
||
discordWebhookBuilder.Content = discordMessageBuilder.Content;
|
||
|
||
var msg = await this.ctx.OriginalComponentInteractionCreateEventArgs.Interaction.EditOriginalResponseAsync(discordWebhookBuilder);
|
||
this.ctx.ResponseMessage = msg;
|
||
return msg;
|
||
}
|
||
|
||
case Enums.CommandType.PrefixCommand:
|
||
case Enums.CommandType.Custom:
|
||
{
|
||
if (this.ctx.ResponseMessage is not null)
|
||
{
|
||
if ((discordMessageBuilder.Files?.Count ?? 0) > 0)
|
||
_ = discordMessageBuilder.KeepAttachments(false);
|
||
|
||
_ = await this.ctx.ResponseMessage.ModifyAsync(discordMessageBuilder);
|
||
this.ctx.ResponseMessage = await this.ctx.ResponseMessage.Refetch();
|
||
|
||
return this.ctx.ResponseMessage;
|
||
}
|
||
|
||
var msg = await this.ctx.Channel.SendMessageAsync(discordMessageBuilder);
|
||
|
||
this.ctx.ResponseMessage = msg;
|
||
|
||
return msg;
|
||
}
|
||
}
|
||
|
||
throw new NotImplementedException();
|
||
}
|
||
#endregion
|
||
|
||
#region GetString
|
||
TVar[] GetDefaultVars()
|
||
=> new TVar[]
|
||
{
|
||
new("CurrentCommand", this.ctx.Prefix + this.ctx.CommandName, false),
|
||
new("Bot", this.ctx.CurrentUser.Mention, false),
|
||
new("BotName", this.ctx.Client.CurrentApplication.Name, false),
|
||
new("FullBot", this.ctx.CurrentUser.GetUsernameWithIdentifier(), false),
|
||
new("BotDisplayName", this.ctx.CurrentUser.GetUsernameWithIdentifier(), false),
|
||
new("User", this.ctx.User.Mention, false),
|
||
new("UserName", this.ctx.User.GetUsername(), false),
|
||
new("FullUser", this.ctx.User.GetUsernameWithIdentifier(), false),
|
||
new("UserDisplayName", this.ctx.Member?.DisplayName ?? this.ctx.User.GetUsername(), false),
|
||
};
|
||
|
||
public string GetString(SingleTranslationKey key)
|
||
=> this.GetString(key, false, Array.Empty<TVar>());
|
||
|
||
public string GetString(SingleTranslationKey key, params TVar[] vars)
|
||
=> this.GetString(key, false, vars);
|
||
|
||
public string GetString(SingleTranslationKey key, bool Code = false, params TVar[] vars)
|
||
=> key.Get(this.ctx.DbUser).Build(Code, vars.Concat(this.GetDefaultVars()).ToArray());
|
||
|
||
|
||
|
||
public string GetString(MultiTranslationKey key)
|
||
=> this.GetString(key, false, false, Array.Empty<TVar>());
|
||
|
||
public string GetString(MultiTranslationKey key, params TVar[] vars)
|
||
=> this.GetString(key, false, false, vars);
|
||
|
||
public string GetString(MultiTranslationKey key, bool Code = false, params TVar[] vars)
|
||
=> this.GetString(key, Code, false, vars);
|
||
|
||
public string GetString(MultiTranslationKey key, bool Code = false, bool UseBoldMarker = false, params TVar[] vars)
|
||
=> key.Get(this.ctx.DbUser).Build(Code, UseBoldMarker, vars.Concat(this.GetDefaultVars()).ToArray());
|
||
|
||
|
||
|
||
public string GetGuildString(SingleTranslationKey key)
|
||
=> this.GetGuildString(key, false, Array.Empty<TVar>());
|
||
|
||
public string GetGuildString(SingleTranslationKey key, params TVar[] vars)
|
||
=> this.GetGuildString(key, false, vars);
|
||
|
||
public string GetGuildString(SingleTranslationKey key, bool Code = false, params TVar[] vars)
|
||
=> key.Get(this.ctx.DbGuild).Build(Code, vars.Concat(this.GetDefaultVars()).ToArray());
|
||
|
||
|
||
|
||
public string GetGuildString(MultiTranslationKey key)
|
||
=> this.GetGuildString(key, false, false, Array.Empty<TVar>());
|
||
|
||
public string GetGuildString(MultiTranslationKey key, params TVar[] vars)
|
||
=> this.GetGuildString(key, false, false, vars);
|
||
|
||
public string GetGuildString(MultiTranslationKey key, bool Code = false, params TVar[] vars)
|
||
=> this.GetGuildString(key, Code, false, vars);
|
||
|
||
public string GetGuildString(MultiTranslationKey key, bool Code = false, bool UseBoldMarker = false, params TVar[] vars)
|
||
=> key.Get(this.ctx.DbGuild).Build(Code, UseBoldMarker, vars.Concat(this.GetDefaultVars()).ToArray());
|
||
#endregion
|
||
|
||
#region Selections
|
||
public async Task<InteractionResult<DiscordRole>> PromptRoleSelection(RolePromptConfiguration configuration = null, TimeSpan? timeOutOverride = null)
|
||
{
|
||
configuration ??= new();
|
||
timeOutOverride ??= TimeSpan.FromSeconds(120);
|
||
|
||
var CreateNewButton = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.CreateRoleForMe), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("➕")));
|
||
var DisableButton = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), configuration.DisableOption ?? this.GetString(this.t.Commands.Common.Prompts.Disable), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("❌")));
|
||
var EveryoneButton = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.SelectEveryone), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👥")));
|
||
var ConfirmSelectionButton = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ConfirmSelection), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✅")));
|
||
|
||
|
||
var SelectionInteractionId = Guid.NewGuid().ToString();
|
||
|
||
DiscordRole FinalSelection = null;
|
||
|
||
var Selected = "";
|
||
|
||
var FinishedSelection = false;
|
||
var ExceptionOccurred = false;
|
||
Exception ThrownException = null;
|
||
|
||
async Task RefreshMessage()
|
||
{
|
||
var dropdown = new DiscordRoleSelectComponent(this.GetString(this.t.Commands.Common.Prompts.SelectARole), SelectionInteractionId, 1, 1, false);
|
||
var builder = new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder(this.ctx.ResponseMessage.Embeds[0]).AsAwaitingInput(this.ctx)).AddComponents(dropdown).WithContent(this.ctx.ResponseMessage.Content);
|
||
|
||
if (Selected.IsNullOrWhiteSpace())
|
||
_ = ConfirmSelectionButton.Disable();
|
||
else
|
||
_ = ConfirmSelectionButton.Enable();
|
||
|
||
List<DiscordComponent> components = new();
|
||
|
||
if (!configuration.CreateRoleOption.IsNullOrWhiteSpace())
|
||
components.Add(CreateNewButton);
|
||
|
||
if (!configuration.DisableOption.IsNullOrWhiteSpace())
|
||
components.Add(DisableButton);
|
||
|
||
if (configuration.IncludeEveryone)
|
||
components.Add(EveryoneButton);
|
||
|
||
if (components.Count != 0)
|
||
_ = builder.AddComponents(components);
|
||
|
||
_ = builder.AddComponents(MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot), ConfirmSelectionButton);
|
||
|
||
_ = await this.RespondOrEdit(builder);
|
||
}
|
||
|
||
_ = RefreshMessage();
|
||
|
||
Stopwatch sw = new();
|
||
sw.Start();
|
||
|
||
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
|
||
{
|
||
_ = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
if (e.Message?.Id == this.ctx.ResponseMessage.Id && e.User.Id == this.ctx.User.Id)
|
||
{
|
||
sw.Restart();
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
|
||
if (e.GetCustomId() == SelectionInteractionId)
|
||
{
|
||
Selected = e.Values[0];
|
||
|
||
try
|
||
{
|
||
var role = this.ctx.Guild.GetRole(Convert.ToUInt64(Selected));
|
||
|
||
if (role.IsManaged || this.ctx.Member.GetRoleHighestPosition() <= role.Position)
|
||
{
|
||
Selected = "";
|
||
_ = e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"❌ {this.GetString(this.t.Commands.Common.Prompts.SelectedRoleUnavailable, true)}"));
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
await RefreshMessage();
|
||
}
|
||
if (e.GetCustomId() == DisableButton.CustomId)
|
||
{
|
||
FinalSelection = null;
|
||
FinishedSelection = true;
|
||
}
|
||
if (e.GetCustomId() == CreateNewButton.CustomId)
|
||
{
|
||
FinalSelection = await this.ctx.Guild.CreateRoleAsync(configuration.CreateRoleOption);
|
||
FinishedSelection = true;
|
||
}
|
||
if (e.GetCustomId() == EveryoneButton.CustomId)
|
||
{
|
||
FinalSelection = this.ctx.Guild.EveryoneRole;
|
||
FinishedSelection = true;
|
||
}
|
||
else if (e.GetCustomId() == ConfirmSelectionButton.CustomId)
|
||
{
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
|
||
FinalSelection = this.ctx.Guild.GetRole(Convert.ToUInt64(Selected));
|
||
FinishedSelection = true;
|
||
}
|
||
else if (e.GetCustomId() == MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot).CustomId)
|
||
throw new CancelException();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ThrownException = ex;
|
||
ExceptionOccurred = true;
|
||
FinishedSelection = true;
|
||
}
|
||
});
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated += RunInteraction;
|
||
|
||
while (!FinishedSelection && sw.Elapsed <= timeOutOverride)
|
||
{
|
||
await Task.Delay(100);
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(this.ctx.ResponseMessage.Embeds[0]).WithContent(this.ctx.ResponseMessage.Content));
|
||
|
||
if (ExceptionOccurred)
|
||
return new InteractionResult<DiscordRole>(ThrownException);
|
||
|
||
return sw.Elapsed >= timeOutOverride
|
||
? new InteractionResult<DiscordRole>(new TimedOutException())
|
||
: new InteractionResult<DiscordRole>(FinalSelection);
|
||
}
|
||
|
||
public Task<InteractionResult<DiscordChannel>> PromptChannelSelection(ChannelType? channelType = null, ChannelPromptConfiguration configuration = null, TimeSpan? timeOutOverride = null)
|
||
=> this.PromptChannelSelection(((channelType is null || !channelType.HasValue) ? null : new ChannelType[] { channelType.Value }), configuration, timeOutOverride);
|
||
|
||
public async Task<InteractionResult<DiscordChannel>> PromptChannelSelection(ChannelType[]? channelTypes = null, ChannelPromptConfiguration configuration = null, TimeSpan? timeOutOverride = null)
|
||
{
|
||
configuration ??= new();
|
||
timeOutOverride ??= TimeSpan.FromSeconds(120);
|
||
|
||
List<DiscordStringSelectComponentOption> FetchedChannels = new();
|
||
|
||
var CreateNewButton = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.CreateChannelForMe), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("➕")));
|
||
var DisableButton = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), configuration.DisableOption ?? this.GetString(this.t.Commands.Common.Prompts.Disable), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("❌")));
|
||
var ConfirmSelectionButton = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ConfirmSelection), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✅")));
|
||
|
||
var SelectionInteractionId = Guid.NewGuid().ToString();
|
||
|
||
DiscordChannel FinalSelection = null;
|
||
|
||
var Selected = "";
|
||
|
||
var FinishedSelection = false;
|
||
var ExceptionOccurred = false;
|
||
Exception ThrownException = null;
|
||
|
||
async Task RefreshMessage()
|
||
{
|
||
var dropdown = new DiscordChannelSelectComponent(this.GetString(this.t.Commands.Common.Prompts.SelectAChannel), channelTypes, SelectionInteractionId);
|
||
var builder = new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder(this.ctx.ResponseMessage.Embeds[0]).AsAwaitingInput(this.ctx)).AddComponents(dropdown).WithContent(this.ctx.ResponseMessage.Content);
|
||
|
||
if (Selected.IsNullOrWhiteSpace())
|
||
_ = ConfirmSelectionButton.Disable();
|
||
else
|
||
_ = ConfirmSelectionButton.Enable();
|
||
|
||
List<DiscordComponent> components = new();
|
||
|
||
if (configuration.CreateChannelOption is not null)
|
||
components.Add(CreateNewButton);
|
||
|
||
if (!configuration.DisableOption.IsNullOrWhiteSpace())
|
||
components.Add(DisableButton);
|
||
|
||
if (components.Count > 0)
|
||
_ = builder.AddComponents(components);
|
||
|
||
_ = builder.AddComponents(MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot), ConfirmSelectionButton);
|
||
|
||
_ = await this.RespondOrEdit(builder);
|
||
}
|
||
|
||
_ = RefreshMessage();
|
||
|
||
Stopwatch sw = new();
|
||
sw.Start();
|
||
|
||
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
|
||
{
|
||
_ = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
if (e.Message?.Id == this.ctx.ResponseMessage.Id && e.User.Id == this.ctx.User.Id)
|
||
{
|
||
sw.Restart();
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
|
||
if (e.GetCustomId() == SelectionInteractionId)
|
||
{
|
||
Selected = e.Values.First();
|
||
FetchedChannels = FetchedChannels.Select(x => new DiscordStringSelectComponentOption(x.Label, x.Value, x.Description, (x.Value == Selected), x.Emoji)).ToList();
|
||
|
||
await RefreshMessage();
|
||
}
|
||
else if (e.GetCustomId() == CreateNewButton.CustomId)
|
||
{
|
||
FinalSelection = await this.ctx.Guild.CreateChannelAsync(configuration.CreateChannelOption.Name, configuration.CreateChannelOption.ChannelType);
|
||
FinishedSelection = true;
|
||
}
|
||
else if (e.GetCustomId() == DisableButton.CustomId)
|
||
{
|
||
FinalSelection = null;
|
||
FinishedSelection = true;
|
||
}
|
||
else if (e.GetCustomId() == ConfirmSelectionButton.CustomId)
|
||
{
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
|
||
FinalSelection = this.ctx.Guild.GetChannel(Convert.ToUInt64(Selected));
|
||
FinishedSelection = true;
|
||
}
|
||
else if (e.GetCustomId() == MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot).CustomId)
|
||
throw new CancelException();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ThrownException = ex;
|
||
ExceptionOccurred = true;
|
||
FinishedSelection = true;
|
||
}
|
||
});
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated += RunInteraction;
|
||
|
||
while (!FinishedSelection && sw.Elapsed <= timeOutOverride)
|
||
{
|
||
await Task.Delay(100);
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(this.ctx.ResponseMessage.Embeds[0]).WithContent(this.ctx.ResponseMessage.Content));
|
||
|
||
if (ExceptionOccurred)
|
||
return new InteractionResult<DiscordChannel>(ThrownException);
|
||
|
||
return sw.Elapsed >= timeOutOverride
|
||
? new InteractionResult<DiscordChannel>(new TimedOutException())
|
||
: new InteractionResult<DiscordChannel>(FinalSelection);
|
||
}
|
||
|
||
public async Task<InteractionResult<string>> PromptCustomSelection(IEnumerable<DiscordStringSelectComponentOption> options, string? CustomPlaceHolder = null, TimeSpan? timeOutOverride = null)
|
||
{
|
||
timeOutOverride ??= TimeSpan.FromSeconds(120);
|
||
CustomPlaceHolder ??= this.GetString(this.t.Commands.Common.Prompts.SelectAnOption);
|
||
|
||
var ConfirmSelectionButton = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ConfirmSelection), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✅")));
|
||
|
||
var PrevPageButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(this.t.Common.PreviousPage), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("◀")));
|
||
var NextPageButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(this.t.Common.NextPage), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("▶")));
|
||
|
||
var CurrentPage = 0;
|
||
var SelectionInteractionId = Guid.NewGuid().ToString();
|
||
|
||
string FinalSelection = null;
|
||
|
||
var Selected = options.FirstOrDefault(x => x.Default, null)?.Value ?? "";
|
||
|
||
var FinishedSelection = false;
|
||
var ExceptionOccurred = false;
|
||
Exception ThrownException = null;
|
||
|
||
while (!Selected.IsNullOrWhiteSpace() && !options.Skip(CurrentPage * 25).Take(25).Any(x => x.Value == Selected))
|
||
{
|
||
if (!options.Skip(CurrentPage * 25).Take(25).Any())
|
||
{
|
||
CurrentPage = 0;
|
||
break;
|
||
}
|
||
|
||
CurrentPage++;
|
||
}
|
||
|
||
async Task RefreshMessage()
|
||
{
|
||
var dropdown = new DiscordStringSelectComponent(CustomPlaceHolder, options.Skip(CurrentPage * 25).Take(25).Select(x => new DiscordStringSelectComponentOption(x.Label, x.Value, x.Description, (x.Value == Selected), x.Emoji)), SelectionInteractionId);
|
||
var builder = new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder(this.ctx.ResponseMessage.Embeds[0]).AsAwaitingInput(this.ctx)).AddComponents(dropdown).WithContent(this.ctx.ResponseMessage.Content);
|
||
|
||
_ = NextPageButton.SetState(options.Skip(CurrentPage * 25).Count() <= 25);
|
||
_ = PrevPageButton.SetState(CurrentPage == 0);
|
||
_ = builder.AddComponents(PrevPageButton, NextPageButton);
|
||
|
||
if (Selected.IsNullOrWhiteSpace())
|
||
_ = ConfirmSelectionButton.Disable();
|
||
else
|
||
_ = ConfirmSelectionButton.Enable();
|
||
|
||
_ = builder.AddComponents(MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot), ConfirmSelectionButton);
|
||
|
||
_ = await this.RespondOrEdit(builder);
|
||
}
|
||
|
||
_ = RefreshMessage();
|
||
|
||
Stopwatch sw = new();
|
||
sw.Start();
|
||
|
||
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
|
||
{
|
||
_ = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
if (e.Message?.Id == this.ctx.ResponseMessage.Id && e.User.Id == this.ctx.User.Id)
|
||
{
|
||
sw.Restart();
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
|
||
if (e.GetCustomId() == SelectionInteractionId)
|
||
{
|
||
Selected = e.Values.First();
|
||
await RefreshMessage();
|
||
}
|
||
else if (e.GetCustomId() == ConfirmSelectionButton.CustomId)
|
||
{
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
|
||
FinalSelection = Selected;
|
||
|
||
FinishedSelection = true;
|
||
}
|
||
else if (e.GetCustomId() == PrevPageButton.CustomId)
|
||
{
|
||
CurrentPage--;
|
||
await RefreshMessage();
|
||
}
|
||
else if (e.GetCustomId() == NextPageButton.CustomId)
|
||
{
|
||
CurrentPage++;
|
||
await RefreshMessage();
|
||
}
|
||
else if (e.GetCustomId() == MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot).CustomId)
|
||
throw new CancelException();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ThrownException = ex;
|
||
ExceptionOccurred = true;
|
||
FinishedSelection = true;
|
||
}
|
||
});
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated += RunInteraction;
|
||
|
||
while (!FinishedSelection && sw.Elapsed <= timeOutOverride)
|
||
{
|
||
await Task.Delay(100);
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(this.ctx.ResponseMessage.Embeds[0]).WithContent(this.ctx.ResponseMessage.Content));
|
||
|
||
if (ExceptionOccurred)
|
||
return new InteractionResult<string>(ThrownException);
|
||
|
||
return sw.Elapsed >= timeOutOverride
|
||
? new InteractionResult<string>(new TimedOutException())
|
||
: new InteractionResult<string>(FinalSelection);
|
||
}
|
||
#endregion
|
||
|
||
#region Modals
|
||
public Task<InteractionResult<ComponentInteractionCreateEventArgs>> PromptModalWithRetry(DiscordInteraction interaction, DiscordInteractionModalBuilder builder, bool ResetToOriginalEmbed = false, TimeSpan? timeOutOverride = null)
|
||
=> this.PromptModalWithRetry(interaction, builder, null, ResetToOriginalEmbed, timeOutOverride);
|
||
|
||
public async Task<InteractionResult<ComponentInteractionCreateEventArgs>> PromptModalWithRetry(DiscordInteraction interaction, DiscordInteractionModalBuilder builder, DiscordEmbedBuilder customEmbed = null, bool ResetToOriginalEmbed = false, TimeSpan? timeOutOverride = null, bool open = true)
|
||
{
|
||
timeOutOverride ??= TimeSpan.FromMinutes(15);
|
||
|
||
var oriEmbed = this.ctx.ResponseMessage.Embeds[0];
|
||
|
||
var ReOpen = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ReOpenModal), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🔄")));
|
||
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(customEmbed ?? new DiscordEmbedBuilder
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Prompts.WaitingForModalResponse, true)
|
||
}.AsAwaitingInput(this.ctx)).AddComponents(new List<DiscordComponent> { ReOpen, MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot) }));
|
||
|
||
ComponentInteractionCreateEventArgs FinishedInteraction = null;
|
||
|
||
var FinishedSelection = false;
|
||
var ExceptionOccurred = false;
|
||
var Cancelled = false;
|
||
Exception ThrownException = null;
|
||
|
||
if (open)
|
||
await interaction.CreateInteractionModalResponseAsync(builder);
|
||
|
||
this.ctx.Client.ComponentInteractionCreated += RunInteraction;
|
||
|
||
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
|
||
{
|
||
_ = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
if (e.Message?.Id == this.ctx.ResponseMessage.Id && e.User.Id == this.ctx.User.Id)
|
||
{
|
||
if (e.GetCustomId() == builder.CustomId)
|
||
{
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
|
||
FinishedInteraction = e;
|
||
FinishedSelection = true;
|
||
}
|
||
else if (e.GetCustomId() == ReOpen.CustomId)
|
||
{
|
||
await e.Interaction.CreateInteractionModalResponseAsync(builder);
|
||
}
|
||
else if (e.GetCustomId() == MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot).CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
throw new CancelException();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ThrownException = ex;
|
||
ExceptionOccurred = true;
|
||
FinishedSelection = true;
|
||
}
|
||
}).Add(this.ctx.Bot, this.ctx);
|
||
}
|
||
|
||
var TimeoutSeconds = (int)(timeOutOverride.Value.TotalSeconds * 2);
|
||
|
||
while (!FinishedSelection && !ExceptionOccurred && !Cancelled && TimeoutSeconds >= 0)
|
||
{
|
||
await Task.Delay(500);
|
||
TimeoutSeconds--;
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||
|
||
if (ResetToOriginalEmbed)
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(oriEmbed));
|
||
|
||
if (ExceptionOccurred)
|
||
return new InteractionResult<ComponentInteractionCreateEventArgs>(ThrownException);
|
||
|
||
return TimeoutSeconds <= 0
|
||
? new InteractionResult<ComponentInteractionCreateEventArgs>(new TimeoutException())
|
||
: new InteractionResult<ComponentInteractionCreateEventArgs>(FinishedInteraction);
|
||
}
|
||
|
||
|
||
public async Task<InteractionResult<TimeSpan>> PromptForTimeSpan(TimeSpan? MaxTime = null, TimeSpan? MinTime = null, TimeSpan? DefaultTime = null, bool ResetToOriginalEmbed = true, TimeSpan? timeOutOverride = null)
|
||
{
|
||
MinTime ??= TimeSpan.Zero;
|
||
MaxTime ??= TimeSpan.FromDays(356);
|
||
DefaultTime ??= TimeSpan.FromSeconds(30);
|
||
timeOutOverride ??= TimeSpan.FromSeconds(300);
|
||
|
||
if (DefaultTime > MaxTime)
|
||
DefaultTime = MaxTime;
|
||
|
||
if (DefaultTime < MinTime)
|
||
DefaultTime = MinTime;
|
||
|
||
var originalEmbed = ResetToOriginalEmbed ? this.ctx.ResponseMessage.Embeds : null;
|
||
|
||
var removeSeconds = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "10s", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var removeSecond = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "1s", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var addSecond = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "1s", false, "➕".UnicodeToEmoji().ToComponent());
|
||
var addSeconds = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "10s", false, "➕".UnicodeToEmoji().ToComponent());
|
||
|
||
var removeMinutes = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "10m", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var removeMinute = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "1m", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var addMinute = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "1m", false, "➕".UnicodeToEmoji().ToComponent());
|
||
var addMinutes = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "10m", false, "➕".UnicodeToEmoji().ToComponent());
|
||
|
||
var removeHours = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "10h", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var removeHour = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "1h", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var addHour = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "1h", false, "➕".UnicodeToEmoji().ToComponent());
|
||
var addHours = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "10h", false, "➕".UnicodeToEmoji().ToComponent());
|
||
|
||
var removeDays = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "10d", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var removeDay = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "1d", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var addDay = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "1d", false, "➕".UnicodeToEmoji().ToComponent());
|
||
var addDays = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "10d", false, "➕".UnicodeToEmoji().ToComponent());
|
||
|
||
var setExact = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ManuallyDefineTimespan), false, "🕒".UnicodeToEmoji().ToComponent());
|
||
var confirmSelection = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ConfirmSelection), false, "✅".UnicodeToEmoji().ToComponent());
|
||
|
||
var previousSuccessSelected = DefaultTime!.Value;
|
||
var currentSelectedTime = DefaultTime!.Value;
|
||
|
||
Task UpdateMessage()
|
||
{
|
||
if (currentSelectedTime > MaxTime || MinTime > currentSelectedTime)
|
||
currentSelectedTime = previousSuccessSelected;
|
||
|
||
previousSuccessSelected = currentSelectedTime;
|
||
|
||
foreach (var button in new List<DiscordButtonComponent>()
|
||
{
|
||
removeSeconds, removeSecond, addSecond, addSeconds,
|
||
removeMinutes, removeMinute, addMinute, addMinutes,
|
||
removeHours, removeHour, addHour, addHours,
|
||
removeDays, removeDay, addDay, addDays
|
||
})
|
||
_ = button.Enable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromSeconds(10) < MinTime)
|
||
_ = removeSeconds.Disable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromSeconds(1) < MinTime)
|
||
_ = removeSecond.Disable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromMinutes(10) < MinTime)
|
||
_ = removeMinutes.Disable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromMinutes(1) < MinTime)
|
||
_ = removeMinute.Disable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromHours(10) < MinTime)
|
||
_ = removeHours.Disable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromHours(1) < MinTime)
|
||
_ = removeHour.Disable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromDays(10) < MinTime)
|
||
_ = removeDays.Disable();
|
||
|
||
if (currentSelectedTime - TimeSpan.FromDays(1) < MinTime)
|
||
_ = removeDay.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromSeconds(10) > MaxTime)
|
||
_ = addSeconds.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromSeconds(1) > MaxTime)
|
||
_ = addSecond.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromMinutes(10) > MaxTime)
|
||
_ = addMinutes.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromMinutes(1) > MaxTime)
|
||
_ = addMinute.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromHours(10) > MaxTime)
|
||
_ = addHours.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromHours(1) > MaxTime)
|
||
_ = addHour.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromDays(10) > MaxTime)
|
||
_ = addDays.Disable();
|
||
|
||
if (currentSelectedTime + TimeSpan.FromDays(1) > MaxTime)
|
||
_ = addDay.Disable();
|
||
|
||
var embed = new DiscordEmbedBuilder()
|
||
.WithDescription($"`{this.GetString(this.t.Commands.Common.Prompts.CurrentTimespan)}`: `{currentSelectedTime.GetHumanReadable(TimeFormat.Days, TranslationUtil.GetTranslatedHumanReadableConfig(this.ctx.DbUser, this.ctx.Bot, true))}`")
|
||
.AsAwaitingInput(this.ctx);
|
||
|
||
|
||
|
||
return this.RespondOrEdit(new DiscordMessageBuilder()
|
||
.AddEmbed(embed)
|
||
.AddComponents(removeSeconds, removeSecond, addSecond, addSeconds)
|
||
.AddComponents(removeMinutes, removeMinute, addMinute, addMinutes)
|
||
.AddComponents(removeHours, removeHour, addHour, addHours)
|
||
.AddComponents(removeDays, removeDay, addDay, addDays)
|
||
.AddComponents(setExact, MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot), confirmSelection));
|
||
}
|
||
await UpdateMessage();
|
||
|
||
var Finished = false;
|
||
var Cancelled = false;
|
||
var timeOut = Stopwatch.StartNew();
|
||
|
||
async Task Interaction(DiscordClient sender, ComponentInteractionCreateEventArgs e)
|
||
{
|
||
_ = Task.Run(async () =>
|
||
{
|
||
if (e.Message?.Id == this.ctx.ResponseMessage?.Id)
|
||
{
|
||
timeOut.Restart();
|
||
|
||
if (e.Id == removeSecond.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromSeconds(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == removeSeconds.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromSeconds(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addSecond.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromSeconds(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addSeconds.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromSeconds(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == removeMinute.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromMinutes(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == removeMinutes.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromMinutes(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addMinute.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromMinutes(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addMinutes.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromMinutes(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == removeHour.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromHours(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == removeHours.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromHours(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addHour.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromHours(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addHours.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromHours(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == removeDay.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromDays(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == removeDays.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromDays(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addDay.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromDays(1));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == addDays.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromDays(10));
|
||
await UpdateMessage();
|
||
}
|
||
else if (e.Id == confirmSelection.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
Finished = true;
|
||
}
|
||
else if (e.Id == MessageComponents.CancelButtonId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
Cancelled = true;
|
||
}
|
||
else if (e.Id == setExact.CustomId)
|
||
{
|
||
|
||
var modal = new DiscordInteractionModalBuilder().WithTitle(this.GetString(this.t.Commands.Common.Prompts.SelectATimeSpan)).WithCustomId(Guid.NewGuid().ToString());
|
||
|
||
if (MaxTime.Value.TotalDays >= 1)
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "days", this.GetString(this.t.Commands.Common.Prompts.TimespanDays)
|
||
.Build(new TVar("Max", ((int)MaxTime.Value.TotalDays))), "0", 1, 3, true, $"{currentSelectedTime.Days}"));
|
||
|
||
if (MaxTime.Value.TotalHours >= 1)
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "hours", this.GetString(this.t.Commands.Common.Prompts.TimespanHours)
|
||
.Build(new TVar("Max", (MaxTime.Value.TotalHours >= 24 ? "23" : $"{((int)MaxTime.Value.TotalHours)}"))), "0", 1, 2, true, $"{currentSelectedTime.Hours}"));
|
||
|
||
if (MaxTime.Value.TotalMinutes >= 1)
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "minutes", this.GetString(this.t.Commands.Common.Prompts.TimespanMinutes)
|
||
.Build(new TVar("Max", (MaxTime.Value.TotalMinutes >= 60 ? "59" : $"{((int)MaxTime.Value.TotalMinutes)}"))), $"0", 1, 2, true, $"{currentSelectedTime.Minutes}"));
|
||
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "seconds", this.GetString(this.t.Commands.Common.Prompts.TimespanSeconds)
|
||
.Build(new TVar("Max", 59)), "0", 1, 2, true, $"{currentSelectedTime.Seconds}"));
|
||
|
||
var ModalResult = await this.PromptModalWithRetry(e.Interaction, modal, true, timeOutOverride.Value.Subtract(timeOut.Elapsed));
|
||
|
||
if (!ModalResult.Failed)
|
||
{
|
||
try
|
||
{
|
||
var Response = ModalResult.Result;
|
||
var modalLength = TimeSpan.FromSeconds(0);
|
||
|
||
if ((Response.Interaction.Data.Components.Any(x => x.CustomId == "seconds") && !Response.Interaction.Data.Components.First(x => x.CustomId == "seconds").Value.IsDigitsOnly()) ||
|
||
(Response.Interaction.Data.Components.Any(x => x.CustomId == "minutes") && !Response.Interaction.Data.Components.First(x => x.CustomId == "minutes").Value.IsDigitsOnly()) ||
|
||
(Response.Interaction.Data.Components.Any(x => x.CustomId == "hours") && !Response.Interaction.Data.Components.First(x => x.CustomId == "hours").Value.IsDigitsOnly()) ||
|
||
(Response.Interaction.Data.Components.Any(x => x.CustomId == "days") && !Response.Interaction.Data.Components.First(x => x.CustomId == "days").Value.IsDigitsOnly()))
|
||
throw new InvalidOperationException("Invalid TimeSpan");
|
||
var seconds = Response.Interaction.Data.Components.Any(x => x.CustomId == "seconds") ? Convert.ToDouble(Convert.ToUInt32(Response.Interaction.Data.Components.First(x => x.CustomId == "seconds").Value)) : 0;
|
||
var minutes = Response.Interaction.Data.Components.Any(x => x.CustomId == "minutes") ? Convert.ToDouble(Convert.ToUInt32(Response.Interaction.Data.Components.First(x => x.CustomId == "minutes").Value)) : 0;
|
||
var hours = Response.Interaction.Data.Components.Any(x => x.CustomId == "hours") ? Convert.ToDouble(Convert.ToUInt32(Response.Interaction.Data.Components.First(x => x.CustomId == "hours").Value)) : 0;
|
||
var days = Response.Interaction.Data.Components.Any(x => x.CustomId == "days") ? Convert.ToDouble(Convert.ToUInt32(Response.Interaction.Data.Components.First(x => x.CustomId == "days").Value)) : 0;
|
||
modalLength = modalLength.Add(TimeSpan.FromSeconds(seconds));
|
||
modalLength = modalLength.Add(TimeSpan.FromMinutes(minutes));
|
||
modalLength = modalLength.Add(TimeSpan.FromHours(hours));
|
||
modalLength = modalLength.Add(TimeSpan.FromDays(days));
|
||
|
||
currentSelectedTime = modalLength;
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
await UpdateMessage();
|
||
}
|
||
}
|
||
}).Add(this.ctx.Bot, this.ctx);
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated += Interaction;
|
||
|
||
while (!Finished && !Cancelled && timeOut.ElapsedMilliseconds < timeOutOverride.Value.TotalMilliseconds)
|
||
await Task.Delay(1000);
|
||
|
||
this.ctx.Client.ComponentInteractionCreated -= Interaction;
|
||
|
||
if (!Finished && !Cancelled && timeOut.ElapsedMilliseconds < timeOutOverride.Value.TotalMilliseconds)
|
||
return new InteractionResult<TimeSpan>(new TimedOutException());
|
||
|
||
if (Cancelled)
|
||
return new InteractionResult<TimeSpan>(new CancelException());
|
||
|
||
if (ResetToOriginalEmbed)
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbeds(originalEmbed));
|
||
|
||
return currentSelectedTime > MaxTime || currentSelectedTime < MinTime
|
||
? new InteractionResult<TimeSpan>(new InvalidOperationException("Invalid TimeSpan"))
|
||
: new InteractionResult<TimeSpan>(currentSelectedTime);
|
||
}
|
||
|
||
public async Task<InteractionResult<DateTime>> PromptModalForDateTime(DateTime? defaultTime = null, bool ResetToOriginalEmbed = true, TimeSpan? timeOutOverride = null)
|
||
{
|
||
timeOutOverride ??= TimeSpan.FromMinutes(2);
|
||
defaultTime ??= DateTime.UtcNow;
|
||
|
||
var originalEmbed = ResetToOriginalEmbed ? this.ctx.ResponseMessage.Embeds : null;
|
||
|
||
var removeMinutes = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "10m", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var removeMinute = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "1m", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var addMinute = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "1m", false, "➕".UnicodeToEmoji().ToComponent());
|
||
var addMinutes = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "10m", false, "➕".UnicodeToEmoji().ToComponent());
|
||
|
||
var removeHours = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "10h", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var removeHour = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "1h", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var addHour = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "1h", false, "➕".UnicodeToEmoji().ToComponent());
|
||
var addHours = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "10h", false, "➕".UnicodeToEmoji().ToComponent());
|
||
|
||
var removeDays = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "10d", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var removeDay = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "1d", false, "➖".UnicodeToEmoji().ToComponent());
|
||
var addDay = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "1d", false, "➕".UnicodeToEmoji().ToComponent());
|
||
var addDays = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "10d", false, "➕".UnicodeToEmoji().ToComponent());
|
||
|
||
var setExact = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ManuallyDefineDateTime), false, "🕒".UnicodeToEmoji().ToComponent());
|
||
var changeTimezone = new DiscordButtonComponent(ButtonStyle.Secondary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.SelectTimezone), false, "🌐".UnicodeToEmoji().ToComponent());
|
||
var confirmSelection = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Common.Prompts.ConfirmSelection), false, "✅".UnicodeToEmoji().ToComponent());
|
||
|
||
var currentSelectedTime = defaultTime!.Value;
|
||
|
||
Task UpdateMessage()
|
||
{
|
||
var embed = new DiscordEmbedBuilder()
|
||
.WithDescription($"`{this.GetString(this.t.Commands.Common.Prompts.CurrentDateTime)}`: {currentSelectedTime.ToTimestamp()} ({currentSelectedTime.ToTimestamp(TimestampFormat.LongDateTime)})")
|
||
.AsAwaitingInput(this.ctx);
|
||
|
||
return this.RespondOrEdit(new DiscordMessageBuilder()
|
||
.AddEmbed(embed)
|
||
.AddComponents(removeMinutes, removeMinute, addMinute, addMinutes)
|
||
.AddComponents(removeHours, removeHour, addHour, addHours)
|
||
.AddComponents(removeDays, removeDay, addDay, addDays)
|
||
.AddComponents(changeTimezone, setExact)
|
||
.AddComponents(MessageComponents.GetCancelButton(this.ctx.DbUser, this.ctx.Bot), confirmSelection));
|
||
}
|
||
await UpdateMessage();
|
||
|
||
var Finished = false;
|
||
var Cancelled = false;
|
||
var timeOut = Stopwatch.StartNew();
|
||
|
||
async Task Interaction(DiscordClient sender, ComponentInteractionCreateEventArgs e)
|
||
{
|
||
_ = Task.Run(async () =>
|
||
{
|
||
if (e.Message?.Id == this.ctx.ResponseMessage?.Id)
|
||
{
|
||
timeOut.Restart();
|
||
|
||
if (e.Id == removeMinute.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromMinutes(1));
|
||
}
|
||
else if (e.Id == removeMinutes.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromMinutes(10));
|
||
}
|
||
else if (e.Id == addMinute.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromMinutes(1));
|
||
}
|
||
else if (e.Id == addMinutes.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromMinutes(10));
|
||
}
|
||
else if (e.Id == removeHour.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromHours(1));
|
||
}
|
||
else if (e.Id == removeHours.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromHours(10));
|
||
}
|
||
else if (e.Id == addHour.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromHours(1));
|
||
}
|
||
else if (e.Id == addHours.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromHours(10));
|
||
}
|
||
else if (e.Id == removeDay.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromDays(1));
|
||
}
|
||
else if (e.Id == removeDays.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Subtract(TimeSpan.FromDays(10));
|
||
}
|
||
else if (e.Id == addDay.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromDays(1));
|
||
}
|
||
else if (e.Id == addDays.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
currentSelectedTime = currentSelectedTime.Add(TimeSpan.FromDays(10));
|
||
}
|
||
else if (e.Id == confirmSelection.CustomId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
Finished = true;
|
||
return;
|
||
}
|
||
else if (e.Id == MessageComponents.CancelButtonId)
|
||
{
|
||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||
Cancelled = true;
|
||
return;
|
||
}
|
||
else if (e.Id == changeTimezone.CustomId)
|
||
{
|
||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Common.Prompts.SelectTimezonePrompt, true)).AsAwaitingInput(this.ctx));
|
||
|
||
var promptResult = await this.PromptCustomSelection(TimeZoneInfo.GetSystemTimeZones()
|
||
.Select(x => new DiscordStringSelectComponentOption(x.DisplayName, x.Id, null, x.Id == (this.ctx.DbUser.Timezone ?? "UTC"))), null, timeOutOverride.Value.Subtract(timeOut.Elapsed));
|
||
|
||
if (promptResult.Failed)
|
||
{
|
||
await UpdateMessage();
|
||
return;
|
||
}
|
||
|
||
this.ctx.DbUser.Timezone = promptResult.Result;
|
||
}
|
||
else if (e.Id == setExact.CustomId)
|
||
{
|
||
var modalInteraction = e.Interaction;
|
||
|
||
if (this.ctx.DbUser.Timezone.IsNullOrWhiteSpace() || !TimeZoneInfo.GetSystemTimeZones().Any(x => x.Id == this.ctx.DbUser.Timezone))
|
||
{
|
||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Common.Prompts.SelectTimezonePrompt, true)).AsAwaitingInput(this.ctx));
|
||
|
||
var promptResult = await this.PromptCustomSelection(TimeZoneInfo.GetSystemTimeZones()
|
||
.Select(x => new DiscordStringSelectComponentOption(x.DisplayName, x.Id, null, x.Id == (this.ctx.DbUser.Timezone ?? "UTC"))), null, timeOutOverride.Value.Subtract(timeOut.Elapsed));
|
||
|
||
if (promptResult.Failed)
|
||
{
|
||
await UpdateMessage();
|
||
return;
|
||
}
|
||
|
||
this.ctx.DbUser.Timezone = promptResult.Result;
|
||
modalInteraction = null;
|
||
}
|
||
|
||
var userTimezone = TimeZoneInfo.FindSystemTimeZoneById(this.ctx.DbUser.Timezone);
|
||
var userTime = TimeZoneInfo.ConvertTimeFromUtc(currentSelectedTime, userTimezone);
|
||
|
||
var modal = new DiscordInteractionModalBuilder().WithTitle(this.GetString(this.t.Commands.Common.Prompts.SelectADateTime)).WithCustomId(Guid.NewGuid().ToString());
|
||
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "minute", this.GetString(this.t.Commands.Common.Prompts.DateTimeMinute), this.GetString(this.t.Commands.Common.Prompts.DateTimeMinute), 1, 2, true, $"{userTime.Minute}"));
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "hour", this.GetString(this.t.Commands.Common.Prompts.DateTimeHour), this.GetString(this.t.Commands.Common.Prompts.DateTimeHour), 1, 2, true, $"{userTime.Hour}"));
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "day", this.GetString(this.t.Commands.Common.Prompts.DateTimeDay), this.GetString(this.t.Commands.Common.Prompts.DateTimeDay), 1, 2, true, $"{userTime.Day}"));
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "month", this.GetString(this.t.Commands.Common.Prompts.DateTimeMonth), this.GetString(this.t.Commands.Common.Prompts.DateTimeMonth), 1, 2, true, $"{userTime.Month}"));
|
||
_ = modal.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "year", this.GetString(this.t.Commands.Common.Prompts.DateTimeYear), this.GetString(this.t.Commands.Common.Prompts.DateTimeYear), 1, 4, true, $"{userTime.Year}"));
|
||
|
||
var ModalResult = await this.PromptModalWithRetry(modalInteraction, modal, null, false, timeOutOverride.Value.Subtract(timeOut.Elapsed), modalInteraction != null);
|
||
|
||
if (ModalResult.Errored)
|
||
{
|
||
await UpdateMessage();
|
||
return;
|
||
}
|
||
|
||
InteractionCreateEventArgs Response = ModalResult.Result;
|
||
|
||
DateTime dateTime;
|
||
|
||
try
|
||
{
|
||
if ((Response.Interaction.Data.Components.Any(x => x.CustomId == "hour") && !Response.Interaction.Data.Components.First(x => x.CustomId == "hour").Value.IsDigitsOnly()) ||
|
||
(Response.Interaction.Data.Components.Any(x => x.CustomId == "minute") && !Response.Interaction.Data.Components.First(x => x.CustomId == "minute").Value.IsDigitsOnly()) ||
|
||
(Response.Interaction.Data.Components.Any(x => x.CustomId == "day") && !Response.Interaction.Data.Components.First(x => x.CustomId == "day").Value.IsDigitsOnly()) ||
|
||
(Response.Interaction.Data.Components.Any(x => x.CustomId == "month") && !Response.Interaction.Data.Components.First(x => x.CustomId == "month").Value.IsDigitsOnly()) ||
|
||
(Response.Interaction.Data.Components.Any(x => x.CustomId == "year") && !Response.Interaction.Data.Components.First(x => x.CustomId == "year").Value.IsDigitsOnly()))
|
||
throw new ArgumentException("Invalid date time");
|
||
|
||
var hour = Convert.ToInt32(Response.Interaction.GetModalValueByCustomId("hour"));
|
||
var minute = Convert.ToInt32(Response.Interaction.GetModalValueByCustomId("minute"));
|
||
var day = Convert.ToInt32(Response.Interaction.GetModalValueByCustomId("day"));
|
||
var month = Convert.ToInt32(Response.Interaction.GetModalValueByCustomId("month"));
|
||
var year = Convert.ToInt32(Response.Interaction.GetModalValueByCustomId("year"));
|
||
|
||
dateTime = TimeZoneInfo.ConvertTimeToUtc(new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Unspecified), userTimezone);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
await UpdateMessage();
|
||
return;
|
||
}
|
||
|
||
currentSelectedTime = dateTime;
|
||
}
|
||
else
|
||
{ return; }
|
||
|
||
await UpdateMessage();
|
||
}
|
||
}).Add(this.ctx.Bot, this.ctx);
|
||
}
|
||
|
||
this.ctx.Client.ComponentInteractionCreated += Interaction;
|
||
|
||
while (!Finished && !Cancelled && timeOut.ElapsedMilliseconds < timeOutOverride.Value.TotalMilliseconds)
|
||
await Task.Delay(1000);
|
||
|
||
this.ctx.Client.ComponentInteractionCreated -= Interaction;
|
||
|
||
if (!Finished && !Cancelled && timeOut.ElapsedMilliseconds < timeOutOverride.Value.TotalMilliseconds)
|
||
return new InteractionResult<DateTime>(new TimedOutException());
|
||
|
||
if (Cancelled)
|
||
return new InteractionResult<DateTime>(new CancelException());
|
||
|
||
if (ResetToOriginalEmbed)
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbeds(originalEmbed));
|
||
|
||
return new InteractionResult<DateTime>(currentSelectedTime);
|
||
}
|
||
#endregion
|
||
|
||
public async Task<(Stream stream, int fileSize)> PromptForFileUpload(TimeSpan? timeOutOverride = null)
|
||
{
|
||
timeOutOverride ??= TimeSpan.FromMinutes(15);
|
||
|
||
if (this.ctx.DbUser.PendingUserUpload is not null)
|
||
{
|
||
if (this.ctx.DbUser.PendingUserUpload.TimeOut.GetTotalSecondsUntil() > 0 && !this.ctx.DbUser.PendingUserUpload.InteractionHandled)
|
||
{
|
||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder
|
||
{
|
||
Description = $"`An upload interaction is already taking place. Please finish it beforehand.`",
|
||
}.AsError(this.ctx)));
|
||
|
||
throw new AlreadyAppliedException("");
|
||
}
|
||
|
||
this.ctx.DbUser.PendingUserUpload = null;
|
||
}
|
||
|
||
this.ctx.DbUser.PendingUserUpload = new UserUpload
|
||
{
|
||
TimeOut = DateTime.UtcNow.Add(timeOutOverride.Value)
|
||
};
|
||
|
||
while (this.ctx.DbUser.PendingUserUpload is not null && !this.ctx.DbUser.PendingUserUpload.InteractionHandled && this.ctx.DbUser.PendingUserUpload.TimeOut.GetTotalSecondsUntil() > 0)
|
||
{
|
||
await Task.Delay(500);
|
||
}
|
||
|
||
if (!this.ctx.DbUser.PendingUserUpload?.InteractionHandled ?? true)
|
||
throw new ArgumentException("");
|
||
|
||
var size = this.ctx.DbUser.PendingUserUpload.FileSize;
|
||
var stream = this.ctx.DbUser.PendingUserUpload.UploadedData;
|
||
|
||
this.ctx.DbUser.PendingUserUpload = null;
|
||
return (stream, size);
|
||
}
|
||
|
||
#region FinishInteraction
|
||
public void ModifyToTimedOut(bool Delete = false)
|
||
{
|
||
_ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder(this.ctx.ResponseMessage.Embeds[0]).WithFooter(this.ctx.ResponseMessage.Embeds[0]?.Footer?.Text + $" • {this.GetString(this.t.Commands.Common.InteractionTimeout)}").WithColor(DiscordColor.Gray)));
|
||
|
||
if (Delete)
|
||
_ = Task.Delay(5000).ContinueWith(_ =>
|
||
{
|
||
if (!this.ctx.ResponseMessage?.Flags.Value.HasMessageFlag(MessageFlags.Ephemeral) ?? false)
|
||
_ = this.ctx.ResponseMessage.DeleteAsync();
|
||
});
|
||
}
|
||
|
||
public void DeleteOrInvalidate()
|
||
{
|
||
switch (this.ctx.CommandType)
|
||
{
|
||
case Enums.CommandType.ContextMenu:
|
||
{
|
||
_ = this.ctx.OriginalContextMenuContext.DeleteResponseAsync();
|
||
break;
|
||
}
|
||
case Enums.CommandType.Event:
|
||
{
|
||
_ = this.ctx.OriginalComponentInteractionCreateEventArgs.Interaction.DeleteOriginalResponseAsync();
|
||
break;
|
||
}
|
||
case Enums.CommandType.ApplicationCommand:
|
||
{
|
||
_ = this.ctx.OriginalInteractionContext.DeleteResponseAsync();
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
_ = this.ctx.ResponseMessage?.DeleteAsync();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region Checks
|
||
public async Task<bool> CheckVoiceState()
|
||
{
|
||
if (this.ctx.Member.VoiceState is null)
|
||
{
|
||
this.SendVoiceStateError();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public async Task<bool> CheckMaintenance()
|
||
{
|
||
if (!this.ctx.User.IsMaintenance(this.ctx.Bot.status))
|
||
{
|
||
this.SendMaintenanceError();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public async Task<bool> CheckBotOwner()
|
||
{
|
||
if (!this.ctx.User.IsMaintenance(this.ctx.Bot.status))
|
||
{
|
||
this.SendBotOwnerError();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public async Task<bool> CheckAdmin()
|
||
{
|
||
if (!this.ctx.Member.IsAdmin(this.ctx.Bot.status))
|
||
{
|
||
this.SendAdminError();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public async Task<bool> CheckPermissions(Permissions perms)
|
||
{
|
||
if (!this.ctx.Member.Permissions.HasPermission(perms))
|
||
{
|
||
this.SendPermissionError(perms);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public async Task<bool> CheckOwnPermissions(Permissions perms)
|
||
{
|
||
if (!this.ctx.CurrentMember.Permissions.HasPermission(perms))
|
||
{
|
||
this.SendOwnPermissionError(perms);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public async Task<bool> CheckSource(Enums.CommandType commandType)
|
||
{
|
||
if (this.ctx.CommandType != commandType)
|
||
{
|
||
this.SendSourceError(commandType);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
#endregion
|
||
|
||
#region ErrorTemplates
|
||
|
||
public void SendDisabledCommandError(string disabledCommand)
|
||
=> _ = this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.CommandDisabled, true, new TVar("Command", disabledCommand))
|
||
}.AsError(this.ctx));
|
||
|
||
public void SendNoMemberError()
|
||
=> _ = this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.NoMember)
|
||
}.AsError(this.ctx));
|
||
|
||
public void SendMaintenanceError()
|
||
=> _ = this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.Generic).Build(true, new TVar("Required", $"{this.ctx.CurrentUser.GetUsername()} Staff"))
|
||
}.AsError(this.ctx));
|
||
|
||
public void SendBotOwnerError()
|
||
=> _ = this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.Generic).Build(true, new TVar("Required", $"<@{this.ctx.Bot.status.TeamOwner}>", false)),
|
||
}.AsError(this.ctx));
|
||
|
||
public void SendAdminError()
|
||
=> _ = this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.Generic).Build(true, new TVar("Required", "Administrator")),
|
||
}.AsError(this.ctx));
|
||
|
||
public void SendPermissionError(Permissions perms)
|
||
=> _ = this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.Generic).Build(true, new TVar("Required", perms.ToTranslatedPermissionString(this.ctx.DbUser, this.ctx.Bot))),
|
||
}.AsError(this.ctx));
|
||
|
||
public void SendVoiceStateError()
|
||
=> _ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.VoiceChannel).Build(true),
|
||
}.AsError(this.ctx)));
|
||
|
||
public void SendUserBanError(BanDetails entry)
|
||
=> _ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder
|
||
{
|
||
Description = this.t.Commands.Common.Errors.UserBan.t["en"].Build(true, new TVar("Reason", entry.Reason)),
|
||
}.AsError(this.ctx)));
|
||
|
||
public void SendGuildBanError(BanDetails entry)
|
||
=> _ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.GuildBan, true, new TVar("Reason", entry.Reason)),
|
||
}.AsError(this.ctx)));
|
||
|
||
public void SendSourceError(Enums.CommandType commandType)
|
||
=> _ = commandType switch
|
||
{
|
||
Enums.CommandType.ApplicationCommand => this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.ExclusiveApp).Build(true),
|
||
}.AsError(this.ctx)),
|
||
Enums.CommandType.PrefixCommand => this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.ExclusivePrefix).Build(true)
|
||
}.AsError(this.ctx)),
|
||
_ => throw new ArgumentException("Invalid Source defined."),
|
||
};
|
||
|
||
public void SendDataError()
|
||
=> _ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.Data, true, new TVar("Command", $"{this.ctx.Prefix}data delete")),
|
||
}.AsError(this.ctx)));
|
||
|
||
public void SendDmError()
|
||
=> _ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder
|
||
{
|
||
Description = $"📩 {this.GetString(this.t.Commands.Common.Errors.DirectMessage, true)}",
|
||
ImageUrl = (this.ctx.User.Presence.ClientStatus.Mobile.HasValue ? "https://cdn.discordapp.com/attachments/1005430437952356423/1144961395515998238/34rhz83ghtzu3ght.gif" : "https://cdn.discordapp.com/attachments/1005430437952356423/1144964670197862400/et2grtzu2ghrzi52.gif")
|
||
}.AsError(this.ctx)));
|
||
|
||
public void SendDmRedirect()
|
||
=> _ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(new DiscordEmbedBuilder
|
||
{
|
||
Description = $"📩 {this.GetString(this.t.Commands.Common.DirectMessageRedirect, true)}",
|
||
}.AsSuccess(this.ctx)));
|
||
|
||
public void SendOwnPermissionError(Permissions perms)
|
||
{
|
||
if (perms is Permissions.AccessChannels or Permissions.SendMessages or Permissions.EmbedLinks)
|
||
return;
|
||
|
||
_ = this.RespondOrEdit(new DiscordEmbedBuilder()
|
||
{
|
||
Description = this.GetString(this.t.Commands.Common.Errors.BotPermissions, true, new TVar("Required", perms.ToTranslatedPermissionString(this.ctx.DbUser, this.ctx.Bot)))
|
||
}.AsError(this.ctx));
|
||
}
|
||
|
||
public void SendSyntaxError()
|
||
{
|
||
if (this.ctx.CommandType != Enums.CommandType.PrefixCommand)
|
||
throw new ArgumentException("Syntax Error can only be generated for Prefix Commands.");
|
||
|
||
var ctx = this.ctx.OriginalCommandContext;
|
||
|
||
var embed = new DiscordEmbedBuilder
|
||
{
|
||
Description = $"**`{ctx.Prefix}{ctx.Command.Name}{(ctx.RawArgumentString != "" ? $" {ctx.RawArgumentString.SanitizeForCode().Replace("\\", "")}" : "")}` is not a valid way of using this command.**\nUse it like this instead: `{ctx.Prefix}{ctx.Command.GenerateUsage()}`\n\nArguments wrapped in `[]` are optional while arguments wrapped in `<>` are required.\n**Do not include the brackets when using commands, they're merely an indicator for requirement.**",
|
||
}.AsError(this.ctx);
|
||
|
||
_ = this.RespondOrEdit(new DiscordMessageBuilder().AddEmbed(embed).WithContent(this.ctx.User.Mention));
|
||
}
|
||
#endregion
|
||
}
|