mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	Applied codestyle to all .cs files
This commit is contained in:
		@@ -5,7 +5,7 @@ namespace NadekoBot.Extensions;
 | 
			
		||||
public static class ArrayExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Create a new array from the old array + new element at the end
 | 
			
		||||
    ///     Create a new array from the old array + new element at the end
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">Input array</param>
 | 
			
		||||
    /// <param name="added">Item to add to the end of the output array</param>
 | 
			
		||||
@@ -14,18 +14,13 @@ public static class ArrayExtensions
 | 
			
		||||
    public static T[] With<T>(this T[] input, T added)
 | 
			
		||||
    {
 | 
			
		||||
        var newCrs = new T[input.Length + 1];
 | 
			
		||||
        Array.Copy(input,
 | 
			
		||||
            0,
 | 
			
		||||
            newCrs,
 | 
			
		||||
            0,
 | 
			
		||||
            input.Length
 | 
			
		||||
        );
 | 
			
		||||
        Array.Copy(input, 0, newCrs, 0, input.Length);
 | 
			
		||||
        newCrs[input.Length] = added;
 | 
			
		||||
        return newCrs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Creates a new array by applying the specified function to every element in the input array
 | 
			
		||||
    ///     Creates a new array by applying the specified function to every element in the input array
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="arr">Array to modify</param>
 | 
			
		||||
    /// <param name="f">Function to apply</param>
 | 
			
		||||
@@ -34,4 +29,4 @@ public static class ArrayExtensions
 | 
			
		||||
    /// <returns>New array with updated elements</returns>
 | 
			
		||||
    public static TOut[] Map<TIn, TOut>(this TIn[] arr, Func<TIn, TOut> f)
 | 
			
		||||
        => Array.ConvertAll(arr, x => f(x));
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -4,4 +4,4 @@ public static class BotCredentialsExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static bool IsOwner(this IBotCredentials creds, IUser user)
 | 
			
		||||
        => creds.OwnerIds.Contains(user.Id);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +1,47 @@
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using NadekoBot.Common.Collections;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Extensions;
 | 
			
		||||
 | 
			
		||||
public static class EnumerableExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Concatenates the members of a collection, using the specified separator between each member.
 | 
			
		||||
    ///     Concatenates the members of a collection, using the specified separator between each member.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="data">Collection to join</param>
 | 
			
		||||
    /// <param name="separator">The character to use as a separator. separator is included in the returned string only if values has more than one element.</param>
 | 
			
		||||
    /// <param name="separator">
 | 
			
		||||
    ///     The character to use as a separator. separator is included in the returned string only if
 | 
			
		||||
    ///     values has more than one element.
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="func">Optional transformation to apply to each element before concatenation.</param>
 | 
			
		||||
    /// <typeparam name="T">The type of the members of values.</typeparam>
 | 
			
		||||
    /// <returns>A string that consists of the members of values delimited by the separator character. -or- Empty if values has no elements.</returns>
 | 
			
		||||
    /// <returns>
 | 
			
		||||
    ///     A string that consists of the members of values delimited by the separator character. -or- Empty if values has
 | 
			
		||||
    ///     no elements.
 | 
			
		||||
    /// </returns>
 | 
			
		||||
    public static string Join<T>(this IEnumerable<T> data, char separator, Func<T, string>? func = null)
 | 
			
		||||
        => string.Join(separator, data.Select(func ?? (x => x?.ToString() ?? string.Empty)));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Concatenates the members of a collection, using the specified separator between each member.
 | 
			
		||||
    ///     Concatenates the members of a collection, using the specified separator between each member.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="data">Collection to join</param>
 | 
			
		||||
    /// <param name="separator">The string to use as a separator.separator is included in the returned string only if values has more than one element.</param>
 | 
			
		||||
    /// <param name="separator">
 | 
			
		||||
    ///     The string to use as a separator.separator is included in the returned string only if values
 | 
			
		||||
    ///     has more than one element.
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <param name="func">Optional transformation to apply to each element before concatenation.</param>
 | 
			
		||||
    /// <typeparam name="T">The type of the members of values.</typeparam>
 | 
			
		||||
    /// <returns>A string that consists of the members of values delimited by the separator character. -or- Empty if values has no elements.</returns>
 | 
			
		||||
    /// <returns>
 | 
			
		||||
    ///     A string that consists of the members of values delimited by the separator character. -or- Empty if values has
 | 
			
		||||
    ///     no elements.
 | 
			
		||||
    /// </returns>
 | 
			
		||||
    public static string Join<T>(this IEnumerable<T> data, string separator, Func<T, string>? func = null)
 | 
			
		||||
        => string.Join(separator, data.Select(func ?? (x => x?.ToString() ?? string.Empty)));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Randomize element order by performing the Fisher-Yates shuffle
 | 
			
		||||
    ///     Randomize element order by performing the Fisher-Yates shuffle
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T">Item type</typeparam>
 | 
			
		||||
    /// <param name="items">Items to shuffle</param>
 | 
			
		||||
@@ -40,13 +52,13 @@ public static class EnumerableExtensions
 | 
			
		||||
        var n = list.Count;
 | 
			
		||||
        while (n > 1)
 | 
			
		||||
        {
 | 
			
		||||
            var box = new byte[(n / Byte.MaxValue) + 1];
 | 
			
		||||
            var box = new byte[(n / byte.MaxValue) + 1];
 | 
			
		||||
            int boxSum;
 | 
			
		||||
            do
 | 
			
		||||
            {
 | 
			
		||||
                provider.GetBytes(box);
 | 
			
		||||
                boxSum = box.Sum(b => b);
 | 
			
		||||
            } while (!(boxSum < n * (Byte.MaxValue * box.Length / n)));
 | 
			
		||||
            } while (!(boxSum < n * (byte.MaxValue * box.Length / n)));
 | 
			
		||||
 | 
			
		||||
            var k = boxSum % n;
 | 
			
		||||
            n--;
 | 
			
		||||
@@ -57,13 +69,16 @@ public static class EnumerableExtensions
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> class
 | 
			
		||||
    /// that contains elements copied from the specified <see cref="IEnumerable{T}" />
 | 
			
		||||
    /// has the default concurrency level, has the default initial capacity,
 | 
			
		||||
    /// and uses the default comparer for the key type.
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}" /> class
 | 
			
		||||
    ///     that contains elements copied from the specified <see cref="IEnumerable{T}" />
 | 
			
		||||
    ///     has the default concurrency level, has the default initial capacity,
 | 
			
		||||
    ///     and uses the default comparer for the key type.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="dict"> The <see cref="IEnumerable{T}" /> whose elements are copied to the new <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param>
 | 
			
		||||
    /// <returns>A new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> class</returns>
 | 
			
		||||
    /// <param name="dict">
 | 
			
		||||
    ///     The <see cref="IEnumerable{T}" /> whose elements are copied to the new
 | 
			
		||||
    ///     <see cref="ConcurrentDictionary{TKey,TValue}" />.
 | 
			
		||||
    /// </param>
 | 
			
		||||
    /// <returns>A new instance of the <see cref="ConcurrentDictionary{TKey,TValue}" /> class</returns>
 | 
			
		||||
    public static ConcurrentDictionary<TKey, TValue> ToConcurrent<TKey, TValue>(
 | 
			
		||||
        this IEnumerable<KeyValuePair<TKey, TValue>> dict)
 | 
			
		||||
        where TKey : notnull
 | 
			
		||||
@@ -76,21 +91,21 @@ public static class EnumerableExtensions
 | 
			
		||||
    // todo use this extension instead of Task.WhenAll
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Creates a task that will complete when all of the <see cref="Task{TResult}"/> objects in an enumerable
 | 
			
		||||
    /// collection have completed
 | 
			
		||||
    ///     Creates a task that will complete when all of the <see cref="Task{TResult}" /> objects in an enumerable
 | 
			
		||||
    ///     collection have completed
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="tasks">The tasks to wait on for completion.</param>
 | 
			
		||||
    /// <typeparam name="TResult">The type of the completed task.</typeparam>
 | 
			
		||||
    /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
 | 
			
		||||
    public static Task<TResult[]> WhenAll<TResult>(this IEnumerable<Task<TResult>> tasks)
 | 
			
		||||
        => Task.WhenAll(tasks);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Creates a task that will complete when all of the <see cref="Task"/> objects in an enumerable
 | 
			
		||||
    /// collection have completed
 | 
			
		||||
    ///     Creates a task that will complete when all of the <see cref="Task" /> objects in an enumerable
 | 
			
		||||
    ///     collection have completed
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="tasks">The tasks to wait on for completion.</param>
 | 
			
		||||
    /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
 | 
			
		||||
    public static Task WhenAll(this IEnumerable<Task> tasks)
 | 
			
		||||
        => Task.WhenAll(tasks);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -13,7 +13,6 @@ using System.Net.Http.Headers;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using Color = Discord.Color;
 | 
			
		||||
using JsonSerializer = System.Text.Json.JsonSerializer;
 | 
			
		||||
 | 
			
		||||
// todo imagesharp extensions
 | 
			
		||||
namespace NadekoBot.Extensions;
 | 
			
		||||
@@ -27,17 +26,15 @@ public static class Extensions
 | 
			
		||||
        => text switch
 | 
			
		||||
        {
 | 
			
		||||
            SmartEmbedText set => msg.ModifyAsync(x =>
 | 
			
		||||
                {
 | 
			
		||||
                    x.Embed = set.GetEmbed().Build();
 | 
			
		||||
                    x.Content = set.PlainText?.SanitizeMentions() ?? "";
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            {
 | 
			
		||||
                x.Embed = set.GetEmbed().Build();
 | 
			
		||||
                x.Content = set.PlainText?.SanitizeMentions() ?? "";
 | 
			
		||||
            }),
 | 
			
		||||
            SmartPlainText spt => msg.ModifyAsync(x =>
 | 
			
		||||
                {
 | 
			
		||||
                    x.Content = spt.Text.SanitizeMentions();
 | 
			
		||||
                    x.Embed = null;
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            {
 | 
			
		||||
                x.Content = spt.Text.SanitizeMentions();
 | 
			
		||||
                x.Embed = null;
 | 
			
		||||
            }),
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -45,8 +42,8 @@ public static class Extensions
 | 
			
		||||
        => client.Guilds.Select(x => x.Id).ToList();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a string in the format HHH:mm if timespan is >= 2m.
 | 
			
		||||
    /// Generates a string in the format 00:mm:ss if timespan is less than 2m.
 | 
			
		||||
    ///     Generates a string in the format HHH:mm if timespan is >= 2m.
 | 
			
		||||
    ///     Generates a string in the format 00:mm:ss if timespan is less than 2m.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="span">Timespan to convert to string</param>
 | 
			
		||||
    /// <returns>Formatted duration string</returns>
 | 
			
		||||
@@ -75,18 +72,14 @@ public static class Extensions
 | 
			
		||||
        var size = ctx.GetCurrentSize();
 | 
			
		||||
        var corners = BuildCorners(size.Width, size.Height, cornerRadius);
 | 
			
		||||
 | 
			
		||||
        ctx.SetGraphicsOptions(new GraphicsOptions()
 | 
			
		||||
            {
 | 
			
		||||
                Antialias = true,
 | 
			
		||||
                // enforces that any part of this shape that has color is punched out of the background
 | 
			
		||||
                AlphaCompositionMode = PixelAlphaCompositionMode.DestOut
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        foreach (var c in corners)
 | 
			
		||||
        ctx.SetGraphicsOptions(new GraphicsOptions
 | 
			
		||||
        {
 | 
			
		||||
            ctx = ctx.Fill(SixLabors.ImageSharp.Color.Red, c);
 | 
			
		||||
        }
 | 
			
		||||
            Antialias = true,
 | 
			
		||||
            // enforces that any part of this shape that has color is punched out of the background
 | 
			
		||||
            AlphaCompositionMode = PixelAlphaCompositionMode.DestOut
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        foreach (var c in corners) ctx = ctx.Fill(SixLabors.ImageSharp.Color.Red, c);
 | 
			
		||||
 | 
			
		||||
        return ctx;
 | 
			
		||||
    }
 | 
			
		||||
@@ -94,11 +87,7 @@ public static class Extensions
 | 
			
		||||
    private static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius)
 | 
			
		||||
    {
 | 
			
		||||
        // first create a square
 | 
			
		||||
        var rect = new RectangularPolygon(-0.5f,
 | 
			
		||||
            -0.5f,
 | 
			
		||||
            cornerRadius,
 | 
			
		||||
            cornerRadius
 | 
			
		||||
        );
 | 
			
		||||
        var rect = new RectangularPolygon(-0.5f, -0.5f, cornerRadius, cornerRadius);
 | 
			
		||||
 | 
			
		||||
        // then cut out of the square a circle so we are left with a corner
 | 
			
		||||
        var cornerTopLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius));
 | 
			
		||||
@@ -114,15 +103,11 @@ public static class Extensions
 | 
			
		||||
        var cornerBottomLeft = cornerTopLeft.RotateDegree(-90).Translate(0, bottomPos);
 | 
			
		||||
        var cornerBottomRight = cornerTopLeft.RotateDegree(180).Translate(rightPos, bottomPos);
 | 
			
		||||
 | 
			
		||||
        return new PathCollection(cornerTopLeft,
 | 
			
		||||
            cornerBottomLeft,
 | 
			
		||||
            cornerTopRight,
 | 
			
		||||
            cornerBottomRight
 | 
			
		||||
        );
 | 
			
		||||
        return new PathCollection(cornerTopLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// First 10 characters of teh bot token.
 | 
			
		||||
    ///     First 10 characters of teh bot token.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static string RedisKey(this IBotCredentials bc)
 | 
			
		||||
        => bc.Token[..10];
 | 
			
		||||
@@ -143,12 +128,11 @@ public static class Extensions
 | 
			
		||||
        ulong? guildId,
 | 
			
		||||
        string prefix)
 | 
			
		||||
        => Array.ConvertAll(strings.GetCommandStrings(cmd.MethodName(), guildId).Args,
 | 
			
		||||
            arg => GetFullUsage(cmd.Name, arg, prefix)
 | 
			
		||||
        );
 | 
			
		||||
            arg => GetFullUsage(cmd.Name, arg, prefix));
 | 
			
		||||
 | 
			
		||||
    private static string MethodName(this CommandInfo cmd)
 | 
			
		||||
        => ((NadekoCommandAttribute?)cmd.Attributes.FirstOrDefault(x => x is NadekoCommandAttribute))?.MethodName ??
 | 
			
		||||
           cmd.Name;
 | 
			
		||||
        => ((NadekoCommandAttribute?)cmd.Attributes.FirstOrDefault(x => x is NadekoCommandAttribute))?.MethodName
 | 
			
		||||
           ?? cmd.Name;
 | 
			
		||||
 | 
			
		||||
    private static string GetFullUsage(string commandName, string args, string prefix)
 | 
			
		||||
        => $"{prefix}{commandName} {string.Format(args, prefix)}";
 | 
			
		||||
@@ -157,8 +141,7 @@ public static class Extensions
 | 
			
		||||
    {
 | 
			
		||||
        if (lastPage != null)
 | 
			
		||||
            return embed.WithFooter($"{curPage + 1} / {lastPage + 1}");
 | 
			
		||||
        else
 | 
			
		||||
            return embed.WithFooter(curPage.ToString());
 | 
			
		||||
        return embed.WithFooter(curPage.ToString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Color ToDiscordColor(this Rgba32 color)
 | 
			
		||||
@@ -205,33 +188,25 @@ public static class Extensions
 | 
			
		||||
        dict.Clear();
 | 
			
		||||
        dict.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
 | 
			
		||||
        dict.Add("User-Agent",
 | 
			
		||||
            "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1"
 | 
			
		||||
        );
 | 
			
		||||
            "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IMessage DeleteAfter(this IUserMessage msg, int seconds, ILogCommandService? logService = null)
 | 
			
		||||
    {
 | 
			
		||||
        Task.Run(async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await Task.Delay(seconds * 1000);
 | 
			
		||||
                if (logService != null)
 | 
			
		||||
                {
 | 
			
		||||
                    logService.AddDeleteIgnore(msg.Id);
 | 
			
		||||
                }
 | 
			
		||||
        {
 | 
			
		||||
            await Task.Delay(seconds * 1000);
 | 
			
		||||
            if (logService != null) logService.AddDeleteIgnore(msg.Id);
 | 
			
		||||
 | 
			
		||||
                try { await msg.DeleteAsync(); }
 | 
			
		||||
                catch { }
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
            try { await msg.DeleteAsync(); }
 | 
			
		||||
            catch { }
 | 
			
		||||
        });
 | 
			
		||||
        return msg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ModuleInfo GetTopLevelModule(this ModuleInfo module)
 | 
			
		||||
    {
 | 
			
		||||
        while (module.Parent != null)
 | 
			
		||||
        {
 | 
			
		||||
            module = module.Parent;
 | 
			
		||||
        }
 | 
			
		||||
        while (module.Parent != null) module = module.Parent;
 | 
			
		||||
 | 
			
		||||
        return module;
 | 
			
		||||
    }
 | 
			
		||||
@@ -246,27 +221,24 @@ public static class Extensions
 | 
			
		||||
        => JsonSerializer.Serialize(any, options);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Adds fallback fonts to <see cref="TextOptions"/>
 | 
			
		||||
    ///     Adds fallback fonts to <see cref="TextOptions" />
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="opts"><see cref="TextOptions"/> to which fallback fonts will be added to</param>
 | 
			
		||||
    /// <param name="opts"><see cref="TextOptions" /> to which fallback fonts will be added to</param>
 | 
			
		||||
    /// <param name="fallback">List of fallback Font Families to add</param>
 | 
			
		||||
    /// <returns>The same <see cref="TextOptions"/> to allow chaining</returns>
 | 
			
		||||
    /// <returns>The same <see cref="TextOptions" /> to allow chaining</returns>
 | 
			
		||||
    public static TextOptions WithFallbackFonts(this TextOptions opts, List<FontFamily> fallback)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var ff in fallback)
 | 
			
		||||
        {
 | 
			
		||||
            opts.FallbackFonts.Add(ff);
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var ff in fallback) opts.FallbackFonts.Add(ff);
 | 
			
		||||
 | 
			
		||||
        return opts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Adds fallback fonts to <see cref="TextGraphicsOptions"/>
 | 
			
		||||
    ///     Adds fallback fonts to <see cref="TextGraphicsOptions" />
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="opts"><see cref="TextGraphicsOptions"/> to which fallback fonts will be added to</param>
 | 
			
		||||
    /// <param name="opts"><see cref="TextGraphicsOptions" /> to which fallback fonts will be added to</param>
 | 
			
		||||
    /// <param name="fallback">List of fallback Font Families to add</param>
 | 
			
		||||
    /// <returns>The same <see cref="TextGraphicsOptions"/> to allow chaining</returns>
 | 
			
		||||
    /// <returns>The same <see cref="TextGraphicsOptions" /> to allow chaining</returns>
 | 
			
		||||
    public static TextGraphicsOptions WithFallbackFonts(this TextGraphicsOptions opts, List<FontFamily> fallback)
 | 
			
		||||
    {
 | 
			
		||||
        opts.TextOptions.WithFallbackFonts(fallback);
 | 
			
		||||
@@ -277,15 +249,13 @@ public static class Extensions
 | 
			
		||||
    {
 | 
			
		||||
        var imageStream = new MemoryStream();
 | 
			
		||||
        if (format?.Name == "GIF")
 | 
			
		||||
        {
 | 
			
		||||
            img.SaveAsGif(imageStream);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            img.SaveAsPng(imageStream,
 | 
			
		||||
                new() { ColorType = PngColorType.RgbWithAlpha, CompressionLevel = PngCompressionLevel.BestCompression }
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
                new()
 | 
			
		||||
                {
 | 
			
		||||
                    ColorType = PngColorType.RgbWithAlpha, CompressionLevel = PngCompressionLevel.BestCompression
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
        imageStream.Position = 0;
 | 
			
		||||
        return imageStream;
 | 
			
		||||
@@ -307,20 +277,14 @@ public static class Extensions
 | 
			
		||||
    public static bool IsImage(this HttpResponseMessage msg, out string? mimeType)
 | 
			
		||||
    {
 | 
			
		||||
        mimeType = msg.Content.Headers.ContentType?.MediaType;
 | 
			
		||||
        if (mimeType is "image/png" or "image/jpeg" or "image/gif")
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (mimeType is "image/png" or "image/jpeg" or "image/gif") return true;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static long? GetImageSize(this HttpResponseMessage msg)
 | 
			
		||||
    {
 | 
			
		||||
        if (msg.Content.Headers.ContentLength is null)
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        if (msg.Content.Headers.ContentLength is null) return null;
 | 
			
		||||
 | 
			
		||||
        return msg.Content.Headers.ContentLength.Value / 1.Mb();
 | 
			
		||||
    }
 | 
			
		||||
@@ -330,4 +294,4 @@ public static class Extensions
 | 
			
		||||
 | 
			
		||||
    public static string GetText(this IBotStrings strings, in LocStr str, CultureInfo culture)
 | 
			
		||||
        => strings.GetText(str.Key, culture, str.Params);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,9 @@ namespace NadekoBot.Extensions;
 | 
			
		||||
 | 
			
		||||
public static class MessageChannelExtensions
 | 
			
		||||
{
 | 
			
		||||
    private static readonly IEmote _arrowLeft = new Emoji("⬅");
 | 
			
		||||
    private static readonly IEmote _arrowRight = new Emoji("➡");
 | 
			
		||||
 | 
			
		||||
    public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, IEmbedBuilder embed, string msg = "")
 | 
			
		||||
        => ch.SendMessageAsync(msg, embed: embed.Build(), options: new() { RetryMode = RetryMode.AlwaysRetry });
 | 
			
		||||
 | 
			
		||||
@@ -37,8 +40,7 @@ public static class MessageChannelExtensions
 | 
			
		||||
    {
 | 
			
		||||
        var embed = eb.Create().WithErrorColor().WithDescription(error).WithTitle(title);
 | 
			
		||||
 | 
			
		||||
        if (url != null &&
 | 
			
		||||
            Uri.IsWellFormedUriString(url, UriKind.Absolute))
 | 
			
		||||
        if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
 | 
			
		||||
            embed.WithUrl(url);
 | 
			
		||||
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(footer))
 | 
			
		||||
@@ -63,8 +65,7 @@ public static class MessageChannelExtensions
 | 
			
		||||
    {
 | 
			
		||||
        var embed = eb.Create().WithOkColor().WithDescription(text).WithTitle(title);
 | 
			
		||||
 | 
			
		||||
        if (url != null &&
 | 
			
		||||
            Uri.IsWellFormedUriString(url, UriKind.Absolute))
 | 
			
		||||
        if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
 | 
			
		||||
            embed.WithUrl(url);
 | 
			
		||||
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(footer))
 | 
			
		||||
@@ -84,23 +85,15 @@ public static class MessageChannelExtensions
 | 
			
		||||
        int columns = 3)
 | 
			
		||||
        => ch.SendMessageAsync($@"{seed}```css
 | 
			
		||||
{string.Join("\n", items.Chunk(columns)
 | 
			
		||||
    .Select(ig => string.Concat(ig.Select(howToPrint))))}
 | 
			
		||||
```"
 | 
			
		||||
        );
 | 
			
		||||
                        .Select(ig => string.Concat(ig.Select(howToPrint))))}
 | 
			
		||||
```");
 | 
			
		||||
 | 
			
		||||
    public static Task<IUserMessage> SendTableAsync<T>(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        IEnumerable<T> items,
 | 
			
		||||
        Func<T, string> howToPrint,
 | 
			
		||||
        int columns = 3)
 | 
			
		||||
        => ch.SendTableAsync("",
 | 
			
		||||
            items,
 | 
			
		||||
            howToPrint,
 | 
			
		||||
            columns
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    private static readonly IEmote _arrowLeft = new Emoji("⬅");
 | 
			
		||||
    private static readonly IEmote _arrowRight = new Emoji("➡");
 | 
			
		||||
        => ch.SendTableAsync("", items, howToPrint, columns);
 | 
			
		||||
 | 
			
		||||
    public static Task SendPaginatedConfirmAsync(
 | 
			
		||||
        this ICommandContext ctx,
 | 
			
		||||
@@ -113,11 +106,10 @@ public static class MessageChannelExtensions
 | 
			
		||||
            x => Task.FromResult(pageFunc(x)),
 | 
			
		||||
            totalElements,
 | 
			
		||||
            itemsPerPage,
 | 
			
		||||
            addPaginatedFooter
 | 
			
		||||
        );
 | 
			
		||||
            addPaginatedFooter);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// danny kamisama
 | 
			
		||||
    ///     danny kamisama
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static async Task SendPaginatedConfirmAsync(
 | 
			
		||||
        this ICommandContext ctx,
 | 
			
		||||
@@ -132,8 +124,7 @@ public static class MessageChannelExtensions
 | 
			
		||||
        var lastPage = (totalElements - 1) / itemsPerPage;
 | 
			
		||||
 | 
			
		||||
        var canPaginate = true;
 | 
			
		||||
        if (ctx.Guild is SocketGuild sg &&
 | 
			
		||||
            !sg.CurrentUser.GetPermissions((IGuildChannel)ctx.Channel).AddReactions)
 | 
			
		||||
        if (ctx.Guild is SocketGuild sg && !sg.CurrentUser.GetPermissions((IGuildChannel)ctx.Channel).AddReactions)
 | 
			
		||||
            canPaginate = false;
 | 
			
		||||
 | 
			
		||||
        if (!canPaginate)
 | 
			
		||||
@@ -143,8 +134,7 @@ public static class MessageChannelExtensions
 | 
			
		||||
 | 
			
		||||
        var msg = await ctx.Channel.EmbedAsync(embed);
 | 
			
		||||
 | 
			
		||||
        if (lastPage == 0 ||
 | 
			
		||||
            !canPaginate)
 | 
			
		||||
        if (lastPage == 0 || !canPaginate)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        await msg.AddReactionAsync(_arrowLeft);
 | 
			
		||||
@@ -197,17 +187,12 @@ public static class MessageChannelExtensions
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (msg.Channel is ITextChannel &&
 | 
			
		||||
                ((SocketGuild)ctx.Guild).CurrentUser.GuildPermissions.ManageMessages)
 | 
			
		||||
            {
 | 
			
		||||
            if (msg.Channel is ITextChannel && ((SocketGuild)ctx.Guild).CurrentUser.GuildPermissions.ManageMessages)
 | 
			
		||||
                await msg.RemoveAllReactionsAsync();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await msg.Reactions.Where(x => x.Value.IsMe)
 | 
			
		||||
                    .Select(x => msg.RemoveReactionAsync(x.Key, ctx.Client.CurrentUser))
 | 
			
		||||
                    .WhenAll();
 | 
			
		||||
            }
 | 
			
		||||
                         .Select(x => msg.RemoveReactionAsync(x.Key, ctx.Client.CurrentUser))
 | 
			
		||||
                         .WhenAll();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
@@ -223,4 +208,4 @@ public static class MessageChannelExtensions
 | 
			
		||||
 | 
			
		||||
    public static Task WarningAsync(this ICommandContext ctx)
 | 
			
		||||
        => ctx.Message.AddReactionAsync(new Emoji("⚠️"));
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -15,4 +15,4 @@ public static class LinkedListExtensions
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -42,12 +42,5 @@ public static class NumberExtensions
 | 
			
		||||
        => number == Math.Truncate(number);
 | 
			
		||||
 | 
			
		||||
    public static DateTimeOffset ToUnixTimestamp(this double number)
 | 
			
		||||
        => new DateTimeOffset(1970,
 | 
			
		||||
            1,
 | 
			
		||||
            1,
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            TimeSpan.Zero
 | 
			
		||||
        ).AddSeconds(number);
 | 
			
		||||
}
 | 
			
		||||
        => new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(number);
 | 
			
		||||
}
 | 
			
		||||
@@ -19,20 +19,13 @@ public static class ProcessExtensions
 | 
			
		||||
    {
 | 
			
		||||
        if (_isWindows)
 | 
			
		||||
        {
 | 
			
		||||
            RunProcessAndWaitForExit("taskkill",
 | 
			
		||||
                $"/T /F /PID {process.Id}",
 | 
			
		||||
                timeout,
 | 
			
		||||
                out _
 | 
			
		||||
            );
 | 
			
		||||
            RunProcessAndWaitForExit("taskkill", $"/T /F /PID {process.Id}", timeout, out _);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var children = new HashSet<int>();
 | 
			
		||||
            GetAllChildIdsUnix(process.Id, children, timeout);
 | 
			
		||||
            foreach (var childId in children)
 | 
			
		||||
            {
 | 
			
		||||
                KillProcessUnix(childId, timeout);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (var childId in children) KillProcessUnix(childId, timeout);
 | 
			
		||||
 | 
			
		||||
            KillProcessUnix(process.Id, timeout);
 | 
			
		||||
        }
 | 
			
		||||
@@ -40,23 +33,15 @@ public static class ProcessExtensions
 | 
			
		||||
 | 
			
		||||
    private static void GetAllChildIdsUnix(int parentId, ISet<int> children, TimeSpan timeout)
 | 
			
		||||
    {
 | 
			
		||||
        var exitCode = RunProcessAndWaitForExit("pgrep",
 | 
			
		||||
            $"-P {parentId}",
 | 
			
		||||
            timeout,
 | 
			
		||||
            out var stdout
 | 
			
		||||
        );
 | 
			
		||||
        var exitCode = RunProcessAndWaitForExit("pgrep", $"-P {parentId}", timeout, out var stdout);
 | 
			
		||||
 | 
			
		||||
        if (exitCode == 0 &&
 | 
			
		||||
            !string.IsNullOrEmpty(stdout))
 | 
			
		||||
        if (exitCode == 0 && !string.IsNullOrEmpty(stdout))
 | 
			
		||||
        {
 | 
			
		||||
            using var reader = new StringReader(stdout);
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                var text = reader.ReadLine();
 | 
			
		||||
                if (text is null)
 | 
			
		||||
                {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (text is null) return;
 | 
			
		||||
 | 
			
		||||
                if (int.TryParse(text, out var id))
 | 
			
		||||
                {
 | 
			
		||||
@@ -69,11 +54,7 @@ public static class ProcessExtensions
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void KillProcessUnix(int processId, TimeSpan timeout)
 | 
			
		||||
        => RunProcessAndWaitForExit("kill",
 | 
			
		||||
            $"-TERM {processId}",
 | 
			
		||||
            timeout,
 | 
			
		||||
            out _
 | 
			
		||||
        );
 | 
			
		||||
        => RunProcessAndWaitForExit("kill", $"-TERM {processId}", timeout, out _);
 | 
			
		||||
 | 
			
		||||
    private static int RunProcessAndWaitForExit(
 | 
			
		||||
        string fileName,
 | 
			
		||||
@@ -94,14 +75,10 @@ public static class ProcessExtensions
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
        if (process.WaitForExit((int)timeout.TotalMilliseconds))
 | 
			
		||||
        {
 | 
			
		||||
            stdout = process.StandardOutput.ReadToEnd();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            process.Kill();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return process.ExitCode;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -54,4 +54,4 @@ public static class Rgba32Extensions
 | 
			
		||||
        canvas.Frames.RemoveFrame(0);
 | 
			
		||||
        return canvas;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using NadekoBot.Modules.Music;
 | 
			
		||||
using NadekoBot.Modules.Music.Resolvers;
 | 
			
		||||
using NadekoBot.Modules.Music.Services;
 | 
			
		||||
using StackExchange.Redis;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Extensions;
 | 
			
		||||
 | 
			
		||||
@@ -12,25 +12,22 @@ public static class ServiceCollectionExtensions
 | 
			
		||||
    public static IServiceCollection AddBotStringsServices(this IServiceCollection services, int totalShards)
 | 
			
		||||
        => totalShards <= 1
 | 
			
		||||
            ? services.AddSingleton<IStringsSource, LocalFileStringsSource>()
 | 
			
		||||
                .AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
 | 
			
		||||
                .AddSingleton<IBotStrings, BotStrings>()
 | 
			
		||||
                      .AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
 | 
			
		||||
                      .AddSingleton<IBotStrings, BotStrings>()
 | 
			
		||||
            : services.AddSingleton<IStringsSource, LocalFileStringsSource>()
 | 
			
		||||
                .AddSingleton<IBotStringsProvider, RedisBotStringsProvider>()
 | 
			
		||||
                .AddSingleton<IBotStrings, BotStrings>();
 | 
			
		||||
                      .AddSingleton<IBotStringsProvider, RedisBotStringsProvider>()
 | 
			
		||||
                      .AddSingleton<IBotStrings, BotStrings>();
 | 
			
		||||
 | 
			
		||||
    public static IServiceCollection AddConfigServices(this IServiceCollection services)
 | 
			
		||||
    {
 | 
			
		||||
        var baseType = typeof(ConfigServiceBase<>);
 | 
			
		||||
 | 
			
		||||
        foreach (var type in Assembly.GetCallingAssembly().ExportedTypes.Where(x => x.IsSealed))
 | 
			
		||||
        {
 | 
			
		||||
            if (type.BaseType?.IsGenericType == true &&
 | 
			
		||||
                type.BaseType.GetGenericTypeDefinition() == baseType)
 | 
			
		||||
            if (type.BaseType?.IsGenericType == true && type.BaseType.GetGenericTypeDefinition() == baseType)
 | 
			
		||||
            {
 | 
			
		||||
                services.AddSingleton(type);
 | 
			
		||||
                services.AddSingleton(x => (IConfigService)x.GetRequiredService(type));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return services;
 | 
			
		||||
    }
 | 
			
		||||
@@ -40,26 +37,23 @@ public static class ServiceCollectionExtensions
 | 
			
		||||
 | 
			
		||||
    public static IServiceCollection AddMusic(this IServiceCollection services)
 | 
			
		||||
        => services.AddSingleton<IMusicService, MusicService>()
 | 
			
		||||
            .AddSingleton<ITrackResolveProvider, TrackResolveProvider>()
 | 
			
		||||
            .AddSingleton<IYoutubeResolver, YtdlYoutubeResolver>()
 | 
			
		||||
            .AddSingleton<ISoundcloudResolver, SoundcloudResolver>()
 | 
			
		||||
            .AddSingleton<ILocalTrackResolver, LocalTrackResolver>()
 | 
			
		||||
            .AddSingleton<IRadioResolver, RadioResolver>()
 | 
			
		||||
            .AddSingleton<ITrackCacher, RedisTrackCacher>()
 | 
			
		||||
            .AddSingleton<YtLoader>()
 | 
			
		||||
            .AddSingleton<IPlaceholderProvider>(svc => svc.GetRequiredService<IMusicService>());
 | 
			
		||||
                   .AddSingleton<ITrackResolveProvider, TrackResolveProvider>()
 | 
			
		||||
                   .AddSingleton<IYoutubeResolver, YtdlYoutubeResolver>()
 | 
			
		||||
                   .AddSingleton<ISoundcloudResolver, SoundcloudResolver>()
 | 
			
		||||
                   .AddSingleton<ILocalTrackResolver, LocalTrackResolver>()
 | 
			
		||||
                   .AddSingleton<IRadioResolver, RadioResolver>()
 | 
			
		||||
                   .AddSingleton<ITrackCacher, RedisTrackCacher>()
 | 
			
		||||
                   .AddSingleton<YtLoader>()
 | 
			
		||||
                   .AddSingleton<IPlaceholderProvider>(svc => svc.GetRequiredService<IMusicService>());
 | 
			
		||||
 | 
			
		||||
    // consider using scrutor, because slightly different versions
 | 
			
		||||
    // of this might be needed in several different places
 | 
			
		||||
    public static IServiceCollection AddSealedSubclassesOf(this IServiceCollection services, Type baseType)
 | 
			
		||||
    {
 | 
			
		||||
        var subTypes = Assembly.GetCallingAssembly()
 | 
			
		||||
            .ExportedTypes.Where(type => type.IsSealed && baseType.IsAssignableFrom(type));
 | 
			
		||||
                               .ExportedTypes.Where(type => type.IsSealed && baseType.IsAssignableFrom(type));
 | 
			
		||||
 | 
			
		||||
        foreach (var subType in subTypes)
 | 
			
		||||
        {
 | 
			
		||||
            services.AddSingleton(baseType, subType);
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var subType in subTypes) services.AddSingleton(baseType, subType);
 | 
			
		||||
 | 
			
		||||
        return services;
 | 
			
		||||
    }
 | 
			
		||||
@@ -70,4 +64,4 @@ public static class ServiceCollectionExtensions
 | 
			
		||||
        services.AddSingleton(ConnectionMultiplexer.Connect(conf));
 | 
			
		||||
        return services;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +1,24 @@
 | 
			
		||||
using NadekoBot.Common.Yml;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using NadekoBot.Common.Yml;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Extensions;
 | 
			
		||||
 | 
			
		||||
public static class StringExtensions
 | 
			
		||||
{
 | 
			
		||||
    private static readonly HashSet<char> _lettersAndDigits = new(Enumerable.Range(48, 10)
 | 
			
		||||
                                                                            .Concat(Enumerable.Range(65, 26))
 | 
			
		||||
                                                                            .Concat(Enumerable.Range(97, 26))
 | 
			
		||||
                                                                            .Select(x => (char)x));
 | 
			
		||||
 | 
			
		||||
    private static readonly Regex _filterRegex = new(@"discord(?:\.gg|\.io|\.me|\.li|(?:app)?\.com\/invite)\/(\w+)",
 | 
			
		||||
        RegexOptions.Compiled | RegexOptions.IgnoreCase);
 | 
			
		||||
 | 
			
		||||
    private static readonly Regex _codePointRegex =
 | 
			
		||||
        new(@"(\\U(?<code>[a-zA-Z0-9]{8})|\\u(?<code>[a-zA-Z0-9]{4})|\\x(?<code>[a-zA-Z0-9]{2}))",
 | 
			
		||||
            RegexOptions.Compiled);
 | 
			
		||||
 | 
			
		||||
    public static string PadBoth(this string str, int length)
 | 
			
		||||
    {
 | 
			
		||||
        var spaces = length - str.Length;
 | 
			
		||||
@@ -17,14 +29,8 @@ public static class StringExtensions
 | 
			
		||||
    public static T? MapJson<T>(this string str)
 | 
			
		||||
        => JsonConvert.DeserializeObject<T>(str);
 | 
			
		||||
 | 
			
		||||
    private static readonly HashSet<char> _lettersAndDigits = new(Enumerable.Range(48, 10)
 | 
			
		||||
        .Concat(Enumerable.Range(65, 26))
 | 
			
		||||
        .Concat(Enumerable.Range(97, 26))
 | 
			
		||||
        .Select(x => (char)x)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    public static string StripHtml(this string input)
 | 
			
		||||
        => Regex.Replace(input, "<.*?>", String.Empty);
 | 
			
		||||
        => Regex.Replace(input, "<.*?>", string.Empty);
 | 
			
		||||
 | 
			
		||||
    public static string? TrimTo(this string? str, int maxLength, bool hideDots = false)
 | 
			
		||||
        => hideDots ? str?.Truncate(maxLength, string.Empty) : str?.Truncate(maxLength);
 | 
			
		||||
@@ -49,15 +55,9 @@ public static class StringExtensions
 | 
			
		||||
        var d = new int[n + 1, m + 1];
 | 
			
		||||
 | 
			
		||||
        // Step 1
 | 
			
		||||
        if (n == 0)
 | 
			
		||||
        {
 | 
			
		||||
            return m;
 | 
			
		||||
        }
 | 
			
		||||
        if (n == 0) return m;
 | 
			
		||||
 | 
			
		||||
        if (m == 0)
 | 
			
		||||
        {
 | 
			
		||||
            return n;
 | 
			
		||||
        }
 | 
			
		||||
        if (m == 0) return n;
 | 
			
		||||
 | 
			
		||||
        // Step 2
 | 
			
		||||
        for (var i = 0; i <= n; d[i, 0] = i++)
 | 
			
		||||
@@ -70,16 +70,14 @@ public static class StringExtensions
 | 
			
		||||
 | 
			
		||||
        // Step 3
 | 
			
		||||
        for (var i = 1; i <= n; i++)
 | 
			
		||||
        {
 | 
			
		||||
            //Step 4
 | 
			
		||||
            for (var j = 1; j <= m; j++)
 | 
			
		||||
            {
 | 
			
		||||
                // Step 5
 | 
			
		||||
                var cost = t[j - 1] == s[i - 1] ? 0 : 1;
 | 
			
		||||
        for (var j = 1; j <= m; j++)
 | 
			
		||||
        {
 | 
			
		||||
            // Step 5
 | 
			
		||||
            var cost = t[j - 1] == s[i - 1] ? 0 : 1;
 | 
			
		||||
 | 
			
		||||
                // Step 6
 | 
			
		||||
                d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
 | 
			
		||||
            }
 | 
			
		||||
            // Step 6
 | 
			
		||||
            d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Step 7
 | 
			
		||||
@@ -96,10 +94,6 @@ public static class StringExtensions
 | 
			
		||||
        return ms;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static readonly Regex _filterRegex = new(@"discord(?:\.gg|\.io|\.me|\.li|(?:app)?\.com\/invite)\/(\w+)",
 | 
			
		||||
        RegexOptions.Compiled | RegexOptions.IgnoreCase
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    public static bool IsDiscordInvite(this string str)
 | 
			
		||||
        => _filterRegex.IsMatch(str);
 | 
			
		||||
 | 
			
		||||
@@ -109,7 +103,7 @@ public static class StringExtensions
 | 
			
		||||
    public static string SanitizeMentions(this string str, bool sanitizeRoleMentions = false)
 | 
			
		||||
    {
 | 
			
		||||
        str = str.Replace("@everyone", "@everyοne", StringComparison.InvariantCultureIgnoreCase)
 | 
			
		||||
            .Replace("@here", "@һere", StringComparison.InvariantCultureIgnoreCase);
 | 
			
		||||
                 .Replace("@here", "@һere", StringComparison.InvariantCultureIgnoreCase);
 | 
			
		||||
        if (sanitizeRoleMentions)
 | 
			
		||||
            str = str.SanitizeRoleMentions();
 | 
			
		||||
 | 
			
		||||
@@ -134,11 +128,6 @@ public static class StringExtensions
 | 
			
		||||
    public static bool IsAlphaNumeric(this string txt)
 | 
			
		||||
        => txt.All(c => _lettersAndDigits.Contains(c));
 | 
			
		||||
 | 
			
		||||
    private static readonly Regex _codePointRegex =
 | 
			
		||||
        new(@"(\\U(?<code>[a-zA-Z0-9]{8})|\\u(?<code>[a-zA-Z0-9]{4})|\\x(?<code>[a-zA-Z0-9]{2}))",
 | 
			
		||||
            RegexOptions.Compiled
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    public static string UnescapeUnicodeCodePoints(this string input)
 | 
			
		||||
        => _codePointRegex.Replace(input,
 | 
			
		||||
            me =>
 | 
			
		||||
@@ -146,6 +135,5 @@ public static class StringExtensions
 | 
			
		||||
                var str = me.Groups["code"].Value;
 | 
			
		||||
                var newString = YamlHelper.UnescapeUnicodeCodePoint(str);
 | 
			
		||||
                return newString;
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
}
 | 
			
		||||
            });
 | 
			
		||||
}
 | 
			
		||||
@@ -35,6 +35,5 @@ public static class UserExtensions
 | 
			
		||||
            ? null
 | 
			
		||||
            : new Uri(usr.AvatarId.StartsWith("a_", StringComparison.InvariantCulture)
 | 
			
		||||
                ? $"{DiscordConfig.CDNUrl}avatars/{usr.UserId}/{usr.AvatarId}.gif"
 | 
			
		||||
                : $"{DiscordConfig.CDNUrl}avatars/{usr.UserId}/{usr.AvatarId}.png"
 | 
			
		||||
            );
 | 
			
		||||
}
 | 
			
		||||
                : $"{DiscordConfig.CDNUrl}avatars/{usr.UserId}/{usr.AvatarId}.png");
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user