diff --git a/CHANGELOG.md b/CHANGELOG.md index 322b72529..39666618a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o -## Unreleased +## [4.1.3] - 06.05.2022 ### Added @@ -28,8 +28,16 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog. - Supports multiple exclusivity groups per message - Supports level requirements - However they can only be added one by one + - Use the following commands for more information + - `.h .reroa` + - `.h .reroli` + - `.h .rerot` + - `.h .rerorm` + - `.h .rerodela` - Pagination is now using buttons instead of reactions - Bot will now support much higher XP values for global and server levels +- [dev] Small change and generation perf improvement for the localized response strings + ### Fixed diff --git a/src/NadekoBot.Generators/LocalizedStringsGenerator.cs b/src/NadekoBot.Generators/LocalizedStringsGenerator.cs index 537b0998a..52d2491bd 100644 --- a/src/NadekoBot.Generators/LocalizedStringsGenerator.cs +++ b/src/NadekoBot.Generators/LocalizedStringsGenerator.cs @@ -62,6 +62,7 @@ namespace NadekoBot.Generators sw.WriteLine("{"); sw.Indent++; + var typedParamStrings = new List(10); foreach (var field in fields) { var matches = Regex.Matches(field.Value, @"{(?\d)[}:]"); @@ -71,20 +72,30 @@ namespace NadekoBot.Generators max = Math.Max(max, int.Parse(match.Groups["num"].Value) + 1); } - List typedParamStrings = new List(); - var paramStrings = string.Empty; + typedParamStrings.Clear(); + var typeParams = new string[max]; + var passedParamString = string.Empty; for (var i = 0; i < max; i++) { - typedParamStrings.Add($"object p{i}"); - paramStrings += $", p{i}"; + typedParamStrings.Add($"in T{i} p{i}"); + passedParamString += $", p{i}"; + typeParams[i] = $"T{i}"; } - var sig = string.Empty; - if(max > 0) + var typeParamStr = string.Empty; + if (max > 0) + { sig = $"({string.Join(", ", typedParamStrings)})"; - - sw.WriteLine($"public static LocStr {field.Name}{sig} => new LocStr(\"{field.Name}\"{paramStrings});"); + typeParamStr = $"<{string.Join(", ", typeParams)}>"; + } + + sw.WriteLine("public static LocStr {0}{1}{2} => new LocStr(\"{3}\"{4});", + field.Name, + typeParamStr, + sig, + field.Name, + passedParamString); } sw.Indent--; diff --git a/src/NadekoBot/Bot.cs b/src/NadekoBot/Bot.cs index 494f41b49..a6c87f713 100644 --- a/src/NadekoBot/Bot.cs +++ b/src/NadekoBot/Bot.cs @@ -67,12 +67,13 @@ public sealed class Bot ? GatewayIntents.All : GatewayIntents.AllUnprivileged, LogGatewayIntentWarnings = false, + FormatUsersInBidirectionalUnicode = false, }); _commandService = new(new() { CaseSensitiveCommands = false, - DefaultRunMode = RunMode.Sync + DefaultRunMode = RunMode.Sync, }); // _interactionService = new(Client.Rest); diff --git a/src/NadekoBot/Common/Interaction/NadekoActionInteraction.cs b/src/NadekoBot/Common/Interaction/NadekoActionInteraction.cs new file mode 100644 index 000000000..72673b265 --- /dev/null +++ b/src/NadekoBot/Common/Interaction/NadekoActionInteraction.cs @@ -0,0 +1,27 @@ +namespace NadekoBot; + +public sealed class NadekoActionInteraction : NadekoOwnInteraction +{ + private readonly NadekoInteractionData _data; + private readonly Func _action; + + public NadekoActionInteraction( + DiscordSocketClient client, + ulong authorId, + NadekoInteractionData data, + Func action + ) + : base(client, authorId) + { + _data = data; + _action = action; + } + + public override string Name + => _data.CustomId; + public override IEmote Emote + => _data.Emote; + + public override Task ExecuteOnActionAsync(SocketMessageComponent smc) + => _action(smc); +} \ No newline at end of file diff --git a/src/NadekoBot/Common/NadekoInteractionBase.cs b/src/NadekoBot/Common/Interaction/NadekoInteraction.cs similarity index 81% rename from src/NadekoBot/Common/NadekoInteractionBase.cs rename to src/NadekoBot/Common/Interaction/NadekoInteraction.cs index d57e9b72c..cb7d0bfb6 100644 --- a/src/NadekoBot/Common/NadekoInteractionBase.cs +++ b/src/NadekoBot/Common/Interaction/NadekoInteraction.cs @@ -1,6 +1,5 @@ namespace NadekoBot; - public abstract class NadekoInteraction { // improvements: @@ -9,20 +8,16 @@ public abstract class NadekoInteraction // - public abstract string Name { get; } public abstract IEmote Emote { get; } - public Func OnAction { get; } protected readonly DiscordSocketClient _client; protected readonly TaskCompletionSource _interactionCompletedSource; - protected ulong _authorId; protected IUserMessage message = null!; - protected NadekoInteraction(DiscordSocketClient client, ulong authorId, Func onAction) + protected NadekoInteraction(DiscordSocketClient client) { _client = client; - _authorId = authorId; - OnAction = onAction; _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously); } @@ -37,6 +32,7 @@ public abstract class NadekoInteraction await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build()); } + protected abstract ValueTask Validate(SocketMessageComponent smc); private async Task OnInteraction(SocketInteraction arg) { if (arg is not SocketMessageComponent smc) @@ -48,15 +44,15 @@ public abstract class NadekoInteraction if (smc.Data.CustomId != Name) return; - if (smc.User.Id != _authorId) + if (!await Validate(smc)) { - await arg.DeferAsync(); + await smc.DeferAsync(); return; } _ = Task.Run(async () => { - await OnAction(smc); + await ExecuteOnActionAsync(smc); // this should only be a thing on single-response buttons _interactionCompletedSource.TrySetResult(true); @@ -76,5 +72,6 @@ public abstract class NadekoInteraction return comp.Build(); } -} - \ No newline at end of file + + public abstract Task ExecuteOnActionAsync(SocketMessageComponent smc); +} \ No newline at end of file diff --git a/src/NadekoBot/Common/Interaction/NadekoInteractionBuilder.cs b/src/NadekoBot/Common/Interaction/NadekoInteractionBuilder.cs new file mode 100644 index 000000000..81d96dda2 --- /dev/null +++ b/src/NadekoBot/Common/Interaction/NadekoInteractionBuilder.cs @@ -0,0 +1,41 @@ +namespace NadekoBot; + +/// +/// Builder class for NadekoInteractions +/// +public class NadekoInteractionBuilder +{ + private NadekoInteractionData? iData; + private Func? action; + // private bool isOwn; + + public NadekoInteractionBuilder WithData(in T data) + where T : NadekoInteractionData + { + iData = data; + return this; + } + + // public NadekoOwnInteractionBuiler WithIsOwn(bool isOwn = true) + // { + // this.isOwn = isOwn; + // return this; + // } + + public NadekoInteractionBuilder WithAction(in Func fn) + { + this.action = fn; + return this; + } + + public NadekoActionInteraction Build(DiscordSocketClient client, ulong userId) + { + if (iData is null) + throw new InvalidOperationException("You have to specify the data before building the interaction"); + + if (action is null) + throw new InvalidOperationException("You have to specify the action before building the interaction"); + + return new(client, userId, iData, action); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Common/Interaction/NadekoInteractionData.cs b/src/NadekoBot/Common/Interaction/NadekoInteractionData.cs new file mode 100644 index 000000000..56e871c46 --- /dev/null +++ b/src/NadekoBot/Common/Interaction/NadekoInteractionData.cs @@ -0,0 +1,8 @@ +namespace NadekoBot; + +/// +/// Represents essential interacation data +/// +/// Emote which will show on a button +/// Custom interaction id +public record NadekoInteractionData(IEmote Emote, string CustomId); \ No newline at end of file diff --git a/src/NadekoBot/Common/Interaction/NadekoOwnInteraction.cs b/src/NadekoBot/Common/Interaction/NadekoOwnInteraction.cs new file mode 100644 index 000000000..f6111b607 --- /dev/null +++ b/src/NadekoBot/Common/Interaction/NadekoOwnInteraction.cs @@ -0,0 +1,15 @@ +namespace NadekoBot; + +/// +/// Interaction which only the author can use +/// +public abstract class NadekoOwnInteraction : NadekoInteraction +{ + protected readonly ulong _authorId; + + protected NadekoOwnInteraction(DiscordSocketClient client, ulong authorId) : base(client) + => _authorId = authorId; + + protected override ValueTask Validate(SocketMessageComponent smc) + => new(smc.User.Id == _authorId); +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs b/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs index 3895cfa27..a0cd16ab1 100644 --- a/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs @@ -54,7 +54,19 @@ public partial class Gambling { var bal = await _bank.GetBalanceAsync(ctx.User.Id); - await ReplyConfirmLocalizedAsync(strs.bank_balance(N(bal))); + var eb = _eb.Create(ctx) + .WithOkColor() + .WithDescription(GetText(strs.bank_balance(N(bal)))); + + try + { + await ctx.User.EmbedAsync(eb); + await ctx.OkAsync(); + } + catch + { + await ReplyErrorLocalizedAsync(strs.unable_to_dm_user); + } } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Gambling/CashInteraction.cs b/src/NadekoBot/Modules/Gambling/CashInteraction.cs new file mode 100644 index 000000000..2628e1cc4 --- /dev/null +++ b/src/NadekoBot/Modules/Gambling/CashInteraction.cs @@ -0,0 +1,17 @@ +#nullable disable +namespace NadekoBot.Modules.Gambling; + +public class CashInteraction +{ + public static NadekoInteractionData Data = + new NadekoInteractionData(new Emoji("🏦"), "cash:bank_show_balance"); + + public static NadekoInteraction CreateInstance( + DiscordSocketClient client, + ulong userId, + Func action) + => new NadekoInteractionBuilder() + .WithData(Data) + .WithAction(action) + .Build(client, userId); +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 048cb3e3c..a9cd4c7ec 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -60,7 +60,7 @@ public partial class Gambling : GamblingModule _cache = cache; _client = client; _bank = bank; - + _enUsCulture = new CultureInfo("en-US", false).NumberFormat; _enUsCulture.NumberDecimalDigits = 0; _enUsCulture.NumberGroupSeparator = " "; @@ -89,8 +89,7 @@ public partial class Gambling : GamblingModule // [21:03] Bob Page: Kinda remids me of US economy var embed = _eb.Create() .WithTitle(GetText(strs.economy_state)) - .AddField(GetText(strs.currency_owned), - N(ec.Cash - ec.Bot)) + .AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot)) .AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%") .AddField(GetText(strs.currency_planted), N(ec.Planted)) .AddField(GetText(strs.owned_waifus_total), N(ec.Waifus)) @@ -237,7 +236,6 @@ public partial class Gambling : GamblingModule var kwumId = new kwum(tr.Id).ToString(); var date = $"#{Format.Code(kwumId)} `〖{GetFormattedCurtrDate(tr)}〗`"; - sb.AppendLine($"\\{change} {date} {Format.Bold(N(tr.Amount))}"); var transactionString = GetHumanReadableTransaction(tr.Type, tr.Extra, tr.OtherId); if (transactionString is not null) @@ -265,8 +263,7 @@ public partial class Gambling : GamblingModule int intId = id; await using var uow = _db.GetDbContext(); - var tr = await uow.CurrencyTransactions - .ToLinqToDBTable() + var tr = await uow.CurrencyTransactions.ToLinqToDBTable() .Where(x => x.Id == intId && x.UserId == ctx.User.Id) .FirstOrDefaultAsync(); @@ -276,8 +273,7 @@ public partial class Gambling : GamblingModule return; } - var eb = _eb.Create(ctx) - .WithOkColor(); + var eb = _eb.Create(ctx).WithOkColor(); eb.WithAuthor(ctx.User); eb.WithTitle(GetText(strs.transaction)); @@ -296,7 +292,6 @@ public partial class Gambling : GamblingModule eb.AddField("Note", tr.Note); } - eb.WithFooter(GetFormattedCurtrDate(tr)); await ctx.Channel.EmbedAsync(eb); @@ -316,31 +311,6 @@ public partial class Gambling : GamblingModule (_, _, ulong userId) => $"{type.Titleize()} - {subType.Titleize()} | [{userId}]", _ => $"{type.Titleize()} - {subType.Titleize()}" }; - - - public sealed class CashInteraction : NadekoInteraction - { - public override string Name - => "CASH_OPEN_BANK"; - - public override IEmote Emote - => new Emoji("🏦"); - - public CashInteraction( - [NotNull] DiscordSocketClient client, - ulong authorId, - Func onAction) - : base(client, authorId, onAction) - { - - } - - public static CashInteraction Create( - DiscordSocketClient client, - ulong userId, - Func onAction) - => new(client, userId, onAction); - } [Cmd] [Priority(0)] @@ -349,30 +319,37 @@ public partial class Gambling : GamblingModule var cur = await GetBalanceStringAsync(userId); await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), cur)); } - + private async Task BankAction(SocketMessageComponent smc) { var balance = await _bank.GetBalanceAsync(ctx.User.Id); - await smc.RespondAsync(GetText(strs.bank_balance(N(balance))), ephemeral: true); + + await N(balance) + .Pipe(strs.bank_balance) + .Pipe(GetText) + .Pipe(text => smc.RespondConfirmAsync(_eb, text, ephemeral: true)); } - + + private NadekoInteraction CreateCashInteraction() + => CashInteraction.CreateInstance(_client, ctx.User.Id, BankAction); + [Cmd] [Priority(1)] public async partial Task Cash([Leftover] IUser user = null) { user ??= ctx.User; var cur = await GetBalanceStringAsync(user.Id); + + var inter = user == ctx.User + ? CreateCashInteraction() + : null; - if (user == ctx.User) - { - var inter = CashInteraction.Create(_client, ctx.User.Id, BankAction); - await ConfirmLocalizedAsync(strs.has(Format.Bold(user.ToString()), cur), inter); - } - else - { - await ConfirmLocalizedAsync(strs.has(Format.Bold(user.ToString()), cur)); - } - + await ConfirmLocalizedAsync( + user.ToString() + .Pipe(Format.Bold) + .With(cur) + .Pipe(strs.has), + inter); } [Cmd] @@ -432,10 +409,7 @@ public partial class Gambling : GamblingModule return; } - await _cs.AddAsync(usr.Id, - amount, - new("award", ctx.User.ToString()!, msg, ctx.User.Id) - ); + await _cs.AddAsync(usr.Id, amount, new("award", ctx.User.ToString()!, msg, ctx.User.Id)); await ReplyConfirmLocalizedAsync(strs.awarded(N(amount), $"<@{usrId}>")); } @@ -449,10 +423,7 @@ public partial class Gambling : GamblingModule await _cs.AddBulkAsync(users.Select(x => x.Id).ToList(), amount, - new("award", - ctx.User.ToString()!, - role.Name, - ctx.User.Id)); + new("award", ctx.User.ToString()!, role.Name, ctx.User.Id)); await ReplyConfirmLocalizedAsync(strs.mass_award(N(amount), Format.Bold(users.Count.ToString()), @@ -469,10 +440,7 @@ public partial class Gambling : GamblingModule await _cs.RemoveBulkAsync(users.Select(x => x.Id).ToList(), amount, - new("take", - ctx.User.ToString()!, - null, - ctx.User.Id)); + new("take", ctx.User.ToString()!, null, ctx.User.Id)); await ReplyConfirmLocalizedAsync(strs.mass_take(N(amount), Format.Bold(users.Count.ToString()), @@ -490,10 +458,7 @@ public partial class Gambling : GamblingModule return; } - var extra = new TxData("take", - ctx.User.ToString()!, - null, - ctx.User.Id); + var extra = new TxData("take", ctx.User.ToString()!, null, ctx.User.Id); if (await _cs.RemoveAsync(user.Id, amount, extra)) { @@ -505,7 +470,6 @@ public partial class Gambling : GamblingModule } } - [Cmd] [OwnerOnly] public async partial Task Take(long amount, [Leftover] ulong usrId) @@ -515,10 +479,7 @@ public partial class Gambling : GamblingModule return; } - var extra = new TxData("take", - ctx.User.ToString()!, - null, - ctx.User.Id); + var extra = new TxData("take", ctx.User.ToString()!, null, ctx.User.Id); if (await _cs.RemoveAsync(usrId, amount, extra)) { @@ -606,10 +567,7 @@ public partial class Gambling : GamblingModule } else { - await rdMsg.ModifyAsync(x => - { - x.Embed = embed.Build(); - }); + await rdMsg.ModifyAsync(x => { x.Embed = embed.Build(); }); } } @@ -659,7 +617,6 @@ public partial class Gambling : GamblingModule var result = br.Roll(); - var str = Format.Bold(ctx.User.ToString()) + Format.Code(GetText(strs.roll(result.Roll))); if (result.Multiplier > 0) { @@ -788,9 +745,7 @@ public partial class Gambling : GamblingModule if (amount > 0) { - if (!await _cs.RemoveAsync(ctx.User.Id, - amount, - new("rps", "bet", ""))) + if (!await _cs.RemoveAsync(ctx.User.Id, amount, new("rps", "bet", ""))) { await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); return; diff --git a/src/NadekoBot/Modules/Gambling/GamblingTopLevelModule.cs b/src/NadekoBot/Modules/Gambling/GamblingTopLevelModule.cs index e872baa70..2b7094faa 100644 --- a/src/NadekoBot/Modules/Gambling/GamblingTopLevelModule.cs +++ b/src/NadekoBot/Modules/Gambling/GamblingTopLevelModule.cs @@ -39,13 +39,19 @@ public abstract class GamblingModule : NadekoModule return true; } + public static string N(long cur, IFormatProvider format) + => cur.ToString("C0", format); + + public static string N(decimal cur, IFormatProvider format) + => cur.ToString("C0", format); + protected string N(long cur) - => cur.ToString("C0", GetFlowersCiInternal()); + => N(cur, GetFlowersCiInternal()); protected string N(decimal cur) - => cur.ToString("C0", GetFlowersCiInternal()); + => N(cur, GetFlowersCiInternal()); - private IFormatProvider GetFlowersCiInternal() + protected IFormatProvider GetFlowersCiInternal() { var flowersCi = (CultureInfo)Culture.Clone(); flowersCi.NumberFormat.CurrencySymbol = CurrencySign; diff --git a/src/NadekoBot/Modules/Xp/Club/Club.cs b/src/NadekoBot/Modules/Xp/Club/Club.cs index d9a3d0e28..cdb123a36 100644 --- a/src/NadekoBot/Modules/Xp/Club/Club.cs +++ b/src/NadekoBot/Modules/Xp/Club/Club.cs @@ -240,7 +240,7 @@ public partial class Xp [Cmd] [Priority(1)] public partial Task ClubAccept(IUser user) - => ClubAccept($"{user.Username}#{user.Discriminator}"); + => ClubAccept(user.ToString()); [Cmd] [Priority(0)] @@ -282,7 +282,7 @@ public partial class Xp [Cmd] [Priority(1)] public partial Task ClubBan([Leftover] IUser user) - => ClubBan($"{user.Username}#{user.Discriminator}"); + => ClubBan(user.ToString()); [Cmd] [Priority(0)] @@ -300,7 +300,7 @@ public partial class Xp [Cmd] [Priority(1)] public partial Task ClubUnBan([Leftover] IUser user) - => ClubUnBan($"{user.Username}#{user.Discriminator}"); + => ClubUnBan(user.ToString()); [Cmd] [Priority(0)] diff --git a/src/NadekoBot/Services/strings/IBotStringsExtensions.cs b/src/NadekoBot/Services/strings/IBotStringsExtensions.cs new file mode 100644 index 000000000..599ea78a3 --- /dev/null +++ b/src/NadekoBot/Services/strings/IBotStringsExtensions.cs @@ -0,0 +1,17 @@ +#nullable disable +using System.Globalization; + +namespace NadekoBot.Services; + +public static class BotStringsExtensions +{ + // this one is for pipe fun, see PipeExtensions.cs + public static string GetText(this IBotStrings strings, in LocStr str, in ulong guildId) + => strings.GetText(str.Key, guildId, str.Params); + + public static string GetText(this IBotStrings strings, in LocStr str, ulong? guildId = null) + => strings.GetText(str.Key, guildId, str.Params); + + public static string GetText(this IBotStrings strings, in LocStr str, CultureInfo culture) + => strings.GetText(str.Key, culture, str.Params); +} \ No newline at end of file diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 99029e161..fc3031d1a 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -214,10 +214,4 @@ public static class Extensions => msg.Content.Headers.ContentLength is long length ? length : long.MaxValue; - - public static string GetText(this IBotStrings strings, in LocStr str, ulong? guildId = null) - => strings.GetText(str.Key, guildId, str.Params); - - public static string GetText(this IBotStrings strings, in LocStr str, CultureInfo culture) - => strings.GetText(str.Key, culture, str.Params); } \ No newline at end of file diff --git a/src/NadekoBot/_Extensions/PipeExtensions.cs b/src/NadekoBot/_Extensions/PipeExtensions.cs new file mode 100644 index 000000000..4eb761a0c --- /dev/null +++ b/src/NadekoBot/_Extensions/PipeExtensions.cs @@ -0,0 +1,23 @@ +namespace NadekoBot.Extensions; + + +public delegate TOut PipeFunc(in TIn a); +public delegate TOut PipeFunc(in TIn1 a, in TIn2 b); + +public static class PipeExtensions +{ + public static TOut Pipe(this TIn a, Func fn) + => fn(a); + + public static TOut Pipe(this TIn a, PipeFunc fn) + => fn(a); + + public static TOut Pipe(this (TIn1, TIn2) a, PipeFunc fn) + => fn(a.Item1, a.Item2); + + public static (TIn, TExtra) With(this TIn a, TExtra b) + => (a, b); + + public static async Task Pipe(this Task a, Func fn) + => fn(await a); +} \ No newline at end of file diff --git a/src/NadekoBot/_Extensions/SocketMessageComponentExtensions.cs b/src/NadekoBot/_Extensions/SocketMessageComponentExtensions.cs new file mode 100644 index 000000000..938d4c699 --- /dev/null +++ b/src/NadekoBot/_Extensions/SocketMessageComponentExtensions.cs @@ -0,0 +1,99 @@ +namespace NadekoBot.Extensions; + +public static class SocketMessageComponentExtensions +{ + public static Task RespondAsync( + this SocketMessageComponent smc, + string? plainText, + Embed? embed = null, + IReadOnlyCollection? embeds = null, + bool sanitizeAll = false, + MessageComponent? components = null, + bool ephemeral = true) + { + plainText = sanitizeAll + ? plainText?.SanitizeAllMentions() ?? "" + : plainText?.SanitizeMentions() ?? ""; + + return smc.RespondAsync(plainText, + embed: embed, + embeds: embeds is null + ? null + : embeds as Embed[] ?? embeds.ToArray(), + components: components, + ephemeral: ephemeral, + options: new() + { + RetryMode = RetryMode.AlwaysRetry + }); + } + + public static Task RespondAsync( + this SocketMessageComponent smc, + SmartText text, + bool sanitizeAll = false, + bool ephemeral = true) + => text switch + { + SmartEmbedText set => smc.RespondAsync(set.PlainText, + set.GetEmbed().Build(), + sanitizeAll: sanitizeAll, + ephemeral: ephemeral), + SmartPlainText st => smc.RespondAsync(st.Text, + default(Embed), + sanitizeAll: sanitizeAll, + ephemeral: ephemeral), + SmartEmbedTextArray arr => smc.RespondAsync(arr.Content, + embeds: arr.GetEmbedBuilders().Map(e => e.Build()), + ephemeral: ephemeral), + _ => throw new ArgumentOutOfRangeException(nameof(text)) + }; + + public static Task EmbedAsync( + this SocketMessageComponent smc, + IEmbedBuilder? embed, + string plainText = "", + IReadOnlyCollection? embeds = null, + NadekoInteraction? inter = null, + bool ephemeral = false) + => smc.RespondAsync(plainText, + embed: embed?.Build(), + embeds: embeds?.Map(x => x.Build())); + + public static Task RespondAsync( + this SocketMessageComponent ch, + IEmbedBuilderService eb, + string text, + MessageType type, + bool ephemeral = false, + NadekoInteraction? inter = null) + { + var builder = eb.Create().WithDescription(text); + + builder = (type switch + { + MessageType.Error => builder.WithErrorColor(), + MessageType.Ok => builder.WithOkColor(), + MessageType.Pending => builder.WithPendingColor(), + _ => throw new ArgumentOutOfRangeException(nameof(type)) + }); + + return ch.EmbedAsync(builder, inter: inter, ephemeral: ephemeral); + } + + // embed title and optional footer overloads + + public static Task RespondErrorAsync( + this SocketMessageComponent smc, + IEmbedBuilderService eb, + string text, + bool ephemeral = false) + => smc.RespondAsync(eb, text, MessageType.Error, ephemeral); + + public static Task RespondConfirmAsync( + this SocketMessageComponent smc, + IEmbedBuilderService eb, + string text, + bool ephemeral = false) + => smc.RespondAsync(eb, text, MessageType.Ok, ephemeral); +} \ No newline at end of file