From 5d2d74b92a633158de6f8ead309dd2c9aeb53c45 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 26 Apr 2022 02:28:51 +0200 Subject: [PATCH] Full support for embed arrays in .greet/.bye, .say and other commands which use embeds - Website to create them is live at eb2.nadeko.bot (it will soon be replacing eb.nadeko.bot) - Embed arrays don't have a plainText property (it's renamed to 'content') - Embed arrays use color hex values instead of an integer - Old embed format will still work - There shouldn't be any breaking changes --- src/NadekoBot/Common/Replacements/Replacer.cs | 80 +++++------ .../Common/SmartText/SmartEmbedText.cs | 131 ++++++++++++------ .../Common/SmartText/SmartEmbedTextArray.cs | 4 +- src/NadekoBot/Common/SmartText/SmartText.cs | 4 +- .../Administration/GreetBye/GreetService.cs | 58 +++++++- .../Utility/Patreon/PatreonRewardsService.cs | 5 +- src/NadekoBot/Services/Currency/IWallet.cs | 1 + .../Services/Impl/BotCredsProvider.cs | 3 +- src/NadekoBot/_Extensions/Extensions.cs | 2 +- .../_Extensions/IMessageChannelExtensions.cs | 2 +- 10 files changed, 185 insertions(+), 105 deletions(-) diff --git a/src/NadekoBot/Common/Replacements/Replacer.cs b/src/NadekoBot/Common/Replacements/Replacer.cs index 0f657bd2d..8c0c84d9f 100644 --- a/src/NadekoBot/Common/Replacements/Replacer.cs +++ b/src/NadekoBot/Common/Replacements/Replacer.cs @@ -34,69 +34,59 @@ public class Replacer public SmartText Replace(SmartText data) => data switch { - SmartEmbedText embedData => Replace(embedData), + SmartEmbedText embedData => Replace(embedData) with + { + PlainText = Replace(embedData.PlainText), + Color = embedData.Color + }, SmartPlainText plain => Replace(plain), SmartEmbedTextArray arr => Replace(arr), _ => throw new ArgumentOutOfRangeException(nameof(data), "Unsupported argument type") }; - public SmartEmbedTextArray Replace(SmartEmbedTextArray embedArr) + private SmartEmbedTextArray Replace(SmartEmbedTextArray embedArr) => new() { - Embeds = embedArr.Embeds.Map(Replace), - PlainText = Replace(embedArr.PlainText) + Embeds = embedArr.Embeds.Map(e => Replace(e) with + { + Color = e.Color + }), + Content = Replace(embedArr.Content) }; - public SmartPlainText Replace(SmartPlainText plainText) - => Replace(plainText.Text); + private SmartPlainText Replace(SmartPlainText plain) + => Replace(plain.Text); - public SmartEmbedText Replace(SmartEmbedText embedData) + private T Replace(T embedData) where T: SmartEmbedTextBase, new() { - var newEmbedData = new SmartEmbedText + var newEmbedData = new T { - PlainText = Replace(embedData.PlainText), Description = Replace(embedData.Description), Title = Replace(embedData.Title), Thumbnail = Replace(embedData.Thumbnail), Image = Replace(embedData.Image), - Url = Replace(embedData.Url) - }; - if (embedData.Author is not null) - { - newEmbedData.Author = new() - { - Name = Replace(embedData.Author.Name), - IconUrl = Replace(embedData.Author.IconUrl) - }; - } - - if (embedData.Fields is not null) - { - var fields = new List(); - foreach (var f in embedData.Fields) - { - var newF = new SmartTextEmbedField + Url = Replace(embedData.Url), + Author = embedData.Author is null + ? null + : new() { - Name = Replace(f.Name), - Value = Replace(f.Value), - Inline = f.Inline - }; - fields.Add(newF); - } - - newEmbedData.Fields = fields.ToArray(); - } - - if (embedData.Footer is not null) - { - newEmbedData.Footer = new() + Name = Replace(embedData.Author.Name), + IconUrl = Replace(embedData.Author.IconUrl) + }, + Fields = embedData.Fields?.Map(f => new SmartTextEmbedField { - Text = Replace(embedData.Footer.Text), - IconUrl = Replace(embedData.Footer.IconUrl) - }; - } - - newEmbedData.Color = embedData.Color; + Name = Replace(f.Name), + Value = Replace(f.Value), + Inline = f.Inline + }), + Footer = embedData.Footer is null + ? null + : new() + { + Text = Replace(embedData.Footer.Text), + IconUrl = Replace(embedData.Footer.IconUrl) + } + }; return newEmbedData; } diff --git a/src/NadekoBot/Common/SmartText/SmartEmbedText.cs b/src/NadekoBot/Common/SmartText/SmartEmbedText.cs index 7569f63c7..aa1296227 100644 --- a/src/NadekoBot/Common/SmartText/SmartEmbedText.cs +++ b/src/NadekoBot/Common/SmartText/SmartEmbedText.cs @@ -1,20 +1,64 @@ -#nullable disable +using SixLabors.ImageSharp.PixelFormats; + +#nullable disable namespace NadekoBot; -public sealed record SmartEmbedText : SmartText +public sealed record SmartEmbedArrayElementText : SmartEmbedTextBase { - public string PlainText { get; set; } - public string Title { get; set; } - public string Description { get; set; } - public string Url { get; set; } - public string Thumbnail { get; set; } - public string Image { get; set; } + public string Color { get; init; } = string.Empty; - public SmartTextEmbedAuthor Author { get; set; } - public SmartTextEmbedFooter Footer { get; set; } - public SmartTextEmbedField[] Fields { get; set; } + public SmartEmbedArrayElementText() : base() + { + + } + + public SmartEmbedArrayElementText(IEmbed eb) : base(eb) + { + + } - public uint Color { get; set; } = 7458112; + protected override EmbedBuilder GetEmbedInternal() + { + var embed = base.GetEmbedInternal(); + return embed.WithColor(Rgba32.ParseHex(Color).ToDiscordColor()); + } +} + +public sealed record SmartEmbedText : SmartEmbedTextBase +{ + public string PlainText { get; init; } + + public uint Color { get; init; } = 7458112; + + public SmartEmbedText() + { + } + + private SmartEmbedText(IEmbed eb, string plainText = null) + : base(eb) + => (PlainText, Color) = (plainText, eb.Color?.RawValue ?? 0); + + public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null) + => new(eb, plainText); + + protected override EmbedBuilder GetEmbedInternal() + { + var embed = base.GetEmbedInternal(); + return embed.WithColor(Color); + } +} + +public abstract record SmartEmbedTextBase : SmartText +{ + public string Title { get; init; } + public string Description { get; init; } + public string Url { get; init; } + public string Thumbnail { get; init; } + public string Image { get; init; } + + public SmartTextEmbedAuthor Author { get; init; } + public SmartTextEmbedFooter Footer { get; init; } + public SmartTextEmbedField[] Fields { get; init; } public bool IsValid => !string.IsNullOrWhiteSpace(Title) @@ -26,36 +70,37 @@ public sealed record SmartEmbedText : SmartText && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) || Fields is { Length: > 0 }; - public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null) + protected SmartEmbedTextBase() { - var set = new SmartEmbedText - { - PlainText = plainText, - Title = eb.Title, - Description = eb.Description, - Url = eb.Url, - Thumbnail = eb.Thumbnail?.Url, - Image = eb.Image?.Url, - Author = eb.Author is { } ea - ? new() - { - Name = ea.Name, - Url = ea.Url, - IconUrl = ea.IconUrl - } - : null, - Footer = eb.Footer is { } ef - ? new() - { - Text = ef.Text, - IconUrl = ef.IconUrl - } - : null - }; - + + } + + protected SmartEmbedTextBase(IEmbed eb) + { + Title = eb.Title; + Description = eb.Description; + Url = eb.Url; + Thumbnail = eb.Thumbnail?.Url; + Image = eb.Image?.Url; + Author = eb.Author is { } ea + ? new() + { + Name = ea.Name, + Url = ea.Url, + IconUrl = ea.IconUrl + } + : null; + Footer = eb.Footer is { } ef + ? new() + { + Text = ef.Text, + IconUrl = ef.IconUrl + } + : null; + if (eb.Fields.Length > 0) { - set.Fields = eb.Fields.Select(field + Fields = eb.Fields.Select(field => new SmartTextEmbedField { Inline = field.Inline, @@ -64,14 +109,14 @@ public sealed record SmartEmbedText : SmartText }) .ToArray(); } - - set.Color = eb.Color?.RawValue ?? 0; - return set; } public EmbedBuilder GetEmbed() + => GetEmbedInternal(); + + protected virtual EmbedBuilder GetEmbedInternal() { - var embed = new EmbedBuilder().WithColor(Color); + var embed = new EmbedBuilder(); if (!string.IsNullOrWhiteSpace(Title)) embed.WithTitle(Title); diff --git a/src/NadekoBot/Common/SmartText/SmartEmbedTextArray.cs b/src/NadekoBot/Common/SmartText/SmartEmbedTextArray.cs index aa250e18b..f12e96483 100644 --- a/src/NadekoBot/Common/SmartText/SmartEmbedTextArray.cs +++ b/src/NadekoBot/Common/SmartText/SmartEmbedTextArray.cs @@ -3,8 +3,8 @@ namespace NadekoBot; public sealed record SmartEmbedTextArray : SmartText { - public string PlainText { get; set; } - public SmartEmbedText[] Embeds { get; set; } + public string Content { get; set; } + public SmartEmbedArrayElementText[] Embeds { get; set; } public bool IsValid => Embeds?.All(x => x.IsValid) ?? false; diff --git a/src/NadekoBot/Common/SmartText/SmartText.cs b/src/NadekoBot/Common/SmartText/SmartText.cs index f8b795e2f..7bc840e25 100644 --- a/src/NadekoBot/Common/SmartText/SmartText.cs +++ b/src/NadekoBot/Common/SmartText/SmartText.cs @@ -30,7 +30,7 @@ public abstract record SmartText SmartPlainText spt => new SmartPlainText(spt.Text + input), SmartEmbedTextArray arr => arr with { - PlainText = arr.PlainText + input + Content = arr.Content + input }, _ => throw new ArgumentOutOfRangeException(nameof(text)) }; @@ -45,7 +45,7 @@ public abstract record SmartText SmartPlainText spt => new SmartPlainText(input + spt.Text), SmartEmbedTextArray arr => arr with { - PlainText = input + arr.PlainText + Content = input + arr.Content }, _ => throw new ArgumentOutOfRangeException(nameof(text)) }; diff --git a/src/NadekoBot/Modules/Administration/GreetBye/GreetService.cs b/src/NadekoBot/Modules/Administration/GreetBye/GreetService.cs index 4541c42c9..4c06a9fbc 100644 --- a/src/NadekoBot/Modules/Administration/GreetBye/GreetService.cs +++ b/src/NadekoBot/Modules/Administration/GreetBye/GreetService.cs @@ -256,15 +256,54 @@ public class GreetService : INService, IReadyExecutor { text = new SmartEmbedText() { - PlainText = pt.Text + Description = pt.Text }; } - - ((SmartEmbedText)text).Footer = new() + else if (text is SmartEmbedText set) { - Text = $"This message was sent from {user.Guild} server.", - IconUrl = user.Guild.IconUrl - }; + text = set with + { + Footer = CreateFooterSource(user) + }; + } + else if (text is SmartEmbedTextArray seta) + { + // if the greet dm message is a text array + var ebElem = seta.Embeds.LastOrDefault(); + if (ebElem is null) + { + // if there are no embeds, add an embed with the footer + text = seta with + { + Embeds = new[] + { + new SmartEmbedArrayElementText() + { + Footer = CreateFooterSource(user) + } + } + }; + } + else + { + // if the maximum amount of embeds is reached, edit the last embed + if (seta.Embeds.Length >= 10) + { + seta.Embeds[^1] = seta.Embeds[^1] with + { + Footer = CreateFooterSource(user) + }; + } + else + { + // if there is less than 10 embeds, add an embed with footer only + seta.Embeds = seta.Embeds.Append(new SmartEmbedArrayElementText() + { + Footer = CreateFooterSource(user) + }).ToArray(); + } + } + } await user.SendAsync(text); } @@ -276,6 +315,13 @@ public class GreetService : INService, IReadyExecutor return true; } + private static SmartTextEmbedFooter CreateFooterSource(IGuildUser user) + => new() + { + Text = $"This message was sent from {user.Guild} server.", + IconUrl = user.Guild.IconUrl + }; + private Task OnUserJoined(IGuildUser user) { _ = Task.Run(async () => diff --git a/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs b/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs index 00fb3cc62..c8372cf54 100644 --- a/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs +++ b/src/NadekoBot/Modules/Utility/Patreon/PatreonRewardsService.cs @@ -84,13 +84,12 @@ public class PatreonRewardsService : INService, IReadyExecutor try { using var http = _httpFactory.CreateClient(); - using var content = new StringContent(string.Empty); using var res = await http.PostAsync("https://www.patreon.com/api/oauth2/token" + "?grant_type=refresh_token" + $"&refresh_token={creds.Patreon.RefreshToken}" + $"&client_id={creds.Patreon.ClientId}" + $"&client_secret={creds.Patreon.ClientSecret}", - content); + null); res.EnsureSuccessStatusCode(); @@ -149,7 +148,7 @@ public class PatreonRewardsService : INService, IReadyExecutor if (!success) return; } - + LastUpdate = DateTime.UtcNow; try { diff --git a/src/NadekoBot/Services/Currency/IWallet.cs b/src/NadekoBot/Services/Currency/IWallet.cs index 6cbe4d980..bbf33c919 100644 --- a/src/NadekoBot/Services/Currency/IWallet.cs +++ b/src/NadekoBot/Services/Currency/IWallet.cs @@ -8,6 +8,7 @@ public interface IWallet public Task Take(long amount, TxData txData); public Task Add(long amount, TxData txData); + // todo message public async Task Transfer( long amount, IWallet to, diff --git a/src/NadekoBot/Services/Impl/BotCredsProvider.cs b/src/NadekoBot/Services/Impl/BotCredsProvider.cs index aa68d46fd..0ae8e204f 100644 --- a/src/NadekoBot/Services/Impl/BotCredsProvider.cs +++ b/src/NadekoBot/Services/Impl/BotCredsProvider.cs @@ -53,9 +53,8 @@ public sealed class BotCredsProvider : IBotCredsProvider _config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true) .AddEnvironmentVariables("NadekoBot_") .Build(); -#if !GLOBAL_NADEKO + _changeToken = ChangeToken.OnChange(() => _config.GetReloadToken(), Reload); -#endif Reload(); } diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 9a86ac4fb..bcb2d7c04 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -26,7 +26,7 @@ public static class Extensions SmartEmbedTextArray set => msg.ModifyAsync(x => { x.Embeds = set.GetEmbedBuilders().Map(eb => eb.Build()); - x.Content = set.PlainText?.SanitizeMentions() ?? ""; + x.Content = set.Content?.SanitizeMentions() ?? ""; }), SmartPlainText spt => msg.ModifyAsync(x => { diff --git a/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs index 06b8ba184..d6ac14222 100644 --- a/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs +++ b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs @@ -30,7 +30,7 @@ public static class MessageChannelExtensions { SmartEmbedText set => channel.SendAsync(set.PlainText, set.GetEmbed().Build(), sanitizeAll: sanitizeAll), SmartPlainText st => channel.SendAsync(st.Text, null, sanitizeAll: sanitizeAll), - SmartEmbedTextArray arr => channel.SendAsync(arr.PlainText, + SmartEmbedTextArray arr => channel.SendAsync(arr.Content, embeds: arr.GetEmbedBuilders().Map(e => e.Build())), _ => throw new ArgumentOutOfRangeException(nameof(text)) };