mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-03 16:24:27 -05:00 
			
		
		
		
	Fixed a nullref message when the bot is loading medusae. Added support for multiple embeds in features which support custom embeds in the form of
{plainText:text-here, embeds: [embedObject, embedObject, embedObject]}
			
			
This commit is contained in:
		@@ -1,4 +1,5 @@
 | 
				
			|||||||
using Cloneable;
 | 
					#nullable enable
 | 
				
			||||||
 | 
					using Cloneable;
 | 
				
			||||||
using NadekoBot.Common.Yml;
 | 
					using NadekoBot.Common.Yml;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Nadeko.Medusa;
 | 
					namespace Nadeko.Medusa;
 | 
				
			||||||
@@ -10,7 +11,7 @@ public sealed partial class MedusaConfig : ICloneable<MedusaConfig>
 | 
				
			|||||||
    public int Version { get; set; } = 1;
 | 
					    public int Version { get; set; } = 1;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Comment("List of medusae automatically loaded at startup")]
 | 
					    [Comment("List of medusae automatically loaded at startup")]
 | 
				
			||||||
    public List<string> Loaded { get; set; }
 | 
					    public List<string>? Loaded { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MedusaConfig()
 | 
					    public MedusaConfig()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ public sealed class MedusaConfigService : ConfigServiceBase<MedusaConfig>, IMedu
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public IReadOnlyCollection<string> GetLoadedMedusae()
 | 
					    public IReadOnlyCollection<string> GetLoadedMedusae()
 | 
				
			||||||
        => Data.Loaded.ToList();
 | 
					        => Data.Loaded?.ToList() ?? new List<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void AddLoadedMedusa(string name)
 | 
					    public void AddLoadedMedusa(string name)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -26,6 +26,9 @@ public sealed class MedusaConfigService : ConfigServiceBase<MedusaConfig>, IMedu
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        ModifyConfig(conf =>
 | 
					        ModifyConfig(conf =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (conf.Loaded is null)
 | 
				
			||||||
 | 
					                conf.Loaded = new();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            if(!conf.Loaded.Contains(name))
 | 
					            if(!conf.Loaded.Contains(name))
 | 
				
			||||||
                conf.Loaded.Add(name);
 | 
					                conf.Loaded.Add(name);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -37,6 +40,9 @@ public sealed class MedusaConfigService : ConfigServiceBase<MedusaConfig>, IMedu
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        ModifyConfig(conf =>
 | 
					        ModifyConfig(conf =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (conf.Loaded is null)
 | 
				
			||||||
 | 
					                conf.Loaded = new();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            conf.Loaded.Remove(name);
 | 
					            conf.Loaded.Remove(name);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,9 +36,17 @@ public class Replacer
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            SmartEmbedText embedData => Replace(embedData),
 | 
					            SmartEmbedText embedData => Replace(embedData),
 | 
				
			||||||
            SmartPlainText plain => Replace(plain),
 | 
					            SmartPlainText plain => Replace(plain),
 | 
				
			||||||
 | 
					            SmartEmbedTextArray arr => Replace(arr), 
 | 
				
			||||||
            _ => throw new ArgumentOutOfRangeException(nameof(data), "Unsupported argument type")
 | 
					            _ => throw new ArgumentOutOfRangeException(nameof(data), "Unsupported argument type")
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SmartEmbedTextArray Replace(SmartEmbedTextArray embedArr)
 | 
				
			||||||
 | 
					        => new()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Embeds = embedArr.Embeds.Map(Replace),
 | 
				
			||||||
 | 
					            PlainText = Replace(embedArr.PlainText)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SmartPlainText Replace(SmartPlainText plainText)
 | 
					    public SmartPlainText Replace(SmartPlainText plainText)
 | 
				
			||||||
        => Replace(plainText.Text);
 | 
					        => Replace(plainText.Text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								src/NadekoBot/Common/SmartText/SmartEmbedTextArray.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/NadekoBot/Common/SmartText/SmartEmbedTextArray.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					namespace NadekoBot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public sealed record SmartEmbedTextArray : SmartText
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public string PlainText { get; set; }
 | 
				
			||||||
 | 
					    public SmartEmbedText[] Embeds { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool IsValid
 | 
				
			||||||
 | 
					        => Embeds?.All(x => x.IsValid) ?? false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public EmbedBuilder[] GetEmbedBuilders()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (Embeds is null)
 | 
				
			||||||
 | 
					            return Array.Empty<EmbedBuilder>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Embeds.Map(em => em.GetEmbed());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void NormalizeFields()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (Embeds is null)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        foreach(var eb in Embeds)
 | 
				
			||||||
 | 
					            eb.NormalizeFields();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
#nullable disable
 | 
					#nullable disable
 | 
				
			||||||
using Newtonsoft.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot;
 | 
					namespace NadekoBot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,6 +11,15 @@ public abstract record SmartText
 | 
				
			|||||||
    public bool IsPlainText
 | 
					    public bool IsPlainText
 | 
				
			||||||
        => this is SmartPlainText;
 | 
					        => this is SmartPlainText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public bool IsEmbedArray
 | 
				
			||||||
 | 
					        => this is SmartEmbedTextArray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static readonly JsonSerializerOptions _opts = new JsonSerializerOptions()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        PropertyNameCaseInsensitive = true,
 | 
				
			||||||
 | 
					        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    public static SmartText operator +(SmartText text, string input)
 | 
					    public static SmartText operator +(SmartText text, string input)
 | 
				
			||||||
        => text switch
 | 
					        => text switch
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -19,6 +28,10 @@ public abstract record SmartText
 | 
				
			|||||||
                PlainText = set.PlainText + input
 | 
					                PlainText = set.PlainText + input
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            SmartPlainText spt => new SmartPlainText(spt.Text + input),
 | 
					            SmartPlainText spt => new SmartPlainText(spt.Text + input),
 | 
				
			||||||
 | 
					            SmartEmbedTextArray arr => arr with
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                PlainText = arr.PlainText + input
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
					            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,27 +43,46 @@ public abstract record SmartText
 | 
				
			|||||||
                PlainText = input + set.PlainText
 | 
					                PlainText = input + set.PlainText
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            SmartPlainText spt => new SmartPlainText(input + spt.Text),
 | 
					            SmartPlainText spt => new SmartPlainText(input + spt.Text),
 | 
				
			||||||
 | 
					            SmartEmbedTextArray arr => arr with
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                PlainText = input + arr.PlainText
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
					            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [CanBeNull]
 | 
				
			||||||
    public static SmartText CreateFrom(string input)
 | 
					    public static SmartText CreateFrom(string input)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (string.IsNullOrWhiteSpace(input) || !input.TrimStart().StartsWith("{"))
 | 
					        if (string.IsNullOrWhiteSpace(input))
 | 
				
			||||||
            return new SmartPlainText(input);
 | 
					            return new SmartPlainText(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var smartEmbedText = JsonConvert.DeserializeObject<SmartEmbedText>(input);
 | 
					            var doc = JsonDocument.Parse(input);
 | 
				
			||||||
 | 
					            var root = doc.RootElement;
 | 
				
			||||||
 | 
					            if (root.ValueKind == JsonValueKind.Object)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (root.TryGetProperty("embeds", out _))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var arr = root.Deserialize<SmartEmbedTextArray>(_opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (smartEmbedText is null)
 | 
					                    if (arr is null)
 | 
				
			||||||
                throw new FormatException();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            smartEmbedText.NormalizeFields();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!smartEmbedText.IsValid)
 | 
					 | 
				
			||||||
                        return new SmartPlainText(input);
 | 
					                        return new SmartPlainText(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return smartEmbedText;
 | 
					                    arr!.NormalizeFields();
 | 
				
			||||||
 | 
					                    return arr;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var obj = root.Deserialize<SmartEmbedText>(_opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (obj is null)
 | 
				
			||||||
 | 
					                    return new SmartPlainText(input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                obj.NormalizeFields();
 | 
				
			||||||
 | 
					                return obj;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return new SmartPlainText(input);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch
 | 
					        catch
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -530,7 +530,7 @@ public class UserPunishService : INService, IReadyExecutor
 | 
				
			|||||||
            return default;
 | 
					            return default;
 | 
				
			||||||
        // if template is an embed, send that embed with replacements
 | 
					        // if template is an embed, send that embed with replacements
 | 
				
			||||||
        // otherwise, treat template as a regular string with replacements
 | 
					        // otherwise, treat template as a regular string with replacements
 | 
				
			||||||
        else if (!SmartText.CreateFrom(template).IsEmbed)
 | 
					        else if (SmartText.CreateFrom(template) is not { IsEmbed: true } or { IsEmbedArray: true })
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            template = JsonConvert.SerializeObject(new
 | 
					            template = JsonConvert.SerializeObject(new
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,8 +81,7 @@ public class HelpService : IExecNoCommand, INService
 | 
				
			|||||||
            em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs));
 | 
					            em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        em.AddField(_strings.GetText(strs.usage),
 | 
					        em.AddField(_strings.GetText(strs.usage),
 | 
				
			||||||
              string.Join("\n",
 | 
					              string.Join("\n", com.RealRemarksArr(_strings,_medusae, culture, prefix).Map(arg => Format.Code(arg))))
 | 
				
			||||||
                  Array.ConvertAll(com.RealRemarksArr(_strings,_medusae, culture, prefix), arg => Format.Code(arg))))
 | 
					 | 
				
			||||||
          .WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild))
 | 
					          .WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild))
 | 
				
			||||||
          .WithOkColor();
 | 
					          .WithOkColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,7 @@ public class RedisBotStringsProvider : IBotStringsProvider
 | 
				
			|||||||
        if (descStr == default)
 | 
					        if (descStr == default)
 | 
				
			||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var args = Array.ConvertAll(argsStr.Split('&'), HttpUtility.UrlDecode);
 | 
					        var args = argsStr.Split('&').Map(HttpUtility.UrlDecode);
 | 
				
			||||||
        return new()
 | 
					        return new()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Args = args,
 | 
					            Args = args,
 | 
				
			||||||
@@ -68,7 +68,7 @@ public class RedisBotStringsProvider : IBotStringsProvider
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var hashFields = localeStrings
 | 
					            var hashFields = localeStrings
 | 
				
			||||||
                             .Select(x => new HashEntry($"{x.Key}::args",
 | 
					                             .Select(x => new HashEntry($"{x.Key}::args",
 | 
				
			||||||
                                 string.Join('&', Array.ConvertAll(x.Value.Args, HttpUtility.UrlEncode))))
 | 
					                                 string.Join('&', x.Value.Args.Map(HttpUtility.UrlEncode))))
 | 
				
			||||||
                             .Concat(localeStrings.Select(x => new HashEntry($"{x.Key}::desc", x.Value.Desc)))
 | 
					                             .Concat(localeStrings.Select(x => new HashEntry($"{x.Key}::desc", x.Value.Desc)))
 | 
				
			||||||
                             .ToArray();
 | 
					                             .ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,11 @@ public static class Extensions
 | 
				
			|||||||
                x.Embed = set.GetEmbed().Build();
 | 
					                x.Embed = set.GetEmbed().Build();
 | 
				
			||||||
                x.Content = set.PlainText?.SanitizeMentions() ?? "";
 | 
					                x.Content = set.PlainText?.SanitizeMentions() ?? "";
 | 
				
			||||||
            }),
 | 
					            }),
 | 
				
			||||||
 | 
					            SmartEmbedTextArray set => msg.ModifyAsync(x =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                x.Embeds = set.GetEmbedBuilders().Map(eb => eb.Build());
 | 
				
			||||||
 | 
					                x.Content = set.PlainText?.SanitizeMentions() ?? "";
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
            SmartPlainText spt => msg.ModifyAsync(x =>
 | 
					            SmartPlainText spt => msg.ModifyAsync(x =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                x.Content = spt.Text.SanitizeMentions();
 | 
					                x.Content = spt.Text.SanitizeMentions();
 | 
				
			||||||
@@ -116,8 +121,7 @@ public static class Extensions
 | 
				
			|||||||
            args = strings.GetCommandStrings(cmd.Summary, culture).Args;
 | 
					            args = strings.GetCommandStrings(cmd.Summary, culture).Args;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return Array.ConvertAll(args,
 | 
					        return args.Map(arg => GetFullUsage(cmd.Name, arg, prefix));
 | 
				
			||||||
            arg => GetFullUsage(cmd.Name, arg, prefix));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static string GetFullUsage(string commandName, string args, string prefix)
 | 
					    private static string GetFullUsage(string commandName, string args, string prefix)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,19 +16,22 @@ public static class MessageChannelExtensions
 | 
				
			|||||||
    public static Task<IUserMessage> SendAsync(
 | 
					    public static Task<IUserMessage> SendAsync(
 | 
				
			||||||
        this IMessageChannel channel,
 | 
					        this IMessageChannel channel,
 | 
				
			||||||
        string? plainText,
 | 
					        string? plainText,
 | 
				
			||||||
        Embed? embed,
 | 
					        Embed? embed = null,
 | 
				
			||||||
 | 
					        Embed[]? embeds = null,
 | 
				
			||||||
        bool sanitizeAll = false)
 | 
					        bool sanitizeAll = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        plainText = sanitizeAll ? plainText?.SanitizeAllMentions() ?? "" : plainText?.SanitizeMentions() ?? "";
 | 
					        plainText = sanitizeAll ? plainText?.SanitizeAllMentions() ?? "" : plainText?.SanitizeMentions() ?? "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return channel.SendMessageAsync(plainText, embed: embed);
 | 
					        return channel.SendMessageAsync(plainText, embed: embed, embeds: embeds);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Task<IUserMessage> SendAsync(this IMessageChannel channel, SmartText text, bool sanitizeAll = false)
 | 
					    public static Task<IUserMessage> SendAsync(this IMessageChannel channel, SmartText text, bool sanitizeAll = false)
 | 
				
			||||||
        => text switch
 | 
					        => text switch
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            SmartEmbedText set => channel.SendAsync(set.PlainText, set.GetEmbed().Build(), sanitizeAll),
 | 
					            SmartEmbedText set => channel.SendAsync(set.PlainText, set.GetEmbed().Build(), sanitizeAll: sanitizeAll),
 | 
				
			||||||
            SmartPlainText st => channel.SendAsync(st.Text, null, sanitizeAll),
 | 
					            SmartPlainText st => channel.SendAsync(st.Text, null, sanitizeAll: sanitizeAll),
 | 
				
			||||||
 | 
					            SmartEmbedTextArray arr => channel.SendAsync(arr.PlainText,
 | 
				
			||||||
 | 
					                embeds: arr.GetEmbedBuilders().Map(e => e.Build())),
 | 
				
			||||||
            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
					            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user