mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	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
This commit is contained in:
		@@ -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>(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<SmartTextEmbedField>();
 | 
			
		||||
            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;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -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 () =>
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ public interface IWallet
 | 
			
		||||
    public Task<bool> Take(long amount, TxData txData);
 | 
			
		||||
    public Task Add(long amount, TxData txData);
 | 
			
		||||
 | 
			
		||||
    // todo message
 | 
			
		||||
    public async Task<bool> Transfer(
 | 
			
		||||
        long amount,
 | 
			
		||||
        IWallet to,
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 =>
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user