diff --git a/src/NadekoBot.Generators/Cloneable/CloneableGenerator.cs b/src/NadekoBot.Generators/Cloneable/CloneableGenerator.cs index b466830f3..cdf607f1e 100644 --- a/src/NadekoBot.Generators/Cloneable/CloneableGenerator.cs +++ b/src/NadekoBot.Generators/Cloneable/CloneableGenerator.cs @@ -158,7 +158,7 @@ namespace {namespaceName} {{ return new {classSymbol.Name} {{ -{string.Join($",{Environment.NewLine}", fieldAssignmentsCodeFast)} +{string.Join(",\n", fieldAssignmentsCodeFast)} }}; }} @@ -174,7 +174,7 @@ namespace {namespaceName} referenceChain.Push(this); var result = new {classSymbol.Name} {{ -{string.Join($",{Environment.NewLine}", fieldAssignmentsCodeSafe)} +{string.Join($",\n", fieldAssignmentsCodeSafe)} }}; referenceChain.Pop(); return result; diff --git a/src/NadekoBot.Generators/Command/CommandAttributesGenerator.cs b/src/NadekoBot.Generators/Command/CommandAttributesGenerator.cs index 952e326b0..ad0141687 100644 --- a/src/NadekoBot.Generators/Command/CommandAttributesGenerator.cs +++ b/src/NadekoBot.Generators/Command/CommandAttributesGenerator.cs @@ -96,57 +96,6 @@ public class CmdAttribute : System.Attribute } } - // private static void GenerateFileFromModel(in SourceProductionContext ctx, IGrouping @group) - // { - // using var sw = new StringWriter(); - // using var tw = new IndentedTextWriter(sw); - // - // foreach (var model in group) - // { - // tw.WriteLine($"public partial {model.ReturnType} {model.MethodName}({string.Join(", ", model.Params)});"); - // } - // - // GenerateFileFromModel(ctx, sw.ToString(), group.Key, group.AsEnumerable()); - // - // - // } - - // private static void GenerateFileFromModel(SourceProductionContext ctx, string innerContent, string groupName, IEnumerable modelsEnum) - // { - // using var sw = new StringWriter(); - // using var tw = new IndentedTextWriter(sw); - // - // var models = modelsEnum.ToList(); - // var referenceModel = models.First(); - // tw.WriteLine($"namespace {referenceModel.Namespace};"); - // - // foreach (var className in referenceModel.ClassNames.Reverse().ToList()) - // { - // tw.WriteLine($"public partial class {className}"); - // tw.WriteLine("{"); - // tw.WriteLine(); - // tw.Indent ++; - // } - // - // foreach (var model in models) - // { - // tw.WriteLine($"public partial {model.ReturnType} {model.MethodName}({string.Join(", ", model.Params)});"); - // } - // - // foreach (var _ in referenceModel.ClassNames) - // { - // tw.Indent --; - // tw.WriteLine("}"); - // } - // - // // tw.Indent--; - // // tw.WriteLine("}"); - // tw.Flush(); - // - // ctx.AddSource($"{groupName}.qwertyus.g.cs", - // SourceText.From(sw.ToString(), Encoding.UTF8)); - // } - private static string GetSourceText(FileModel model) { using var sw = new StringWriter(); diff --git a/src/NadekoBot/Common/NadekoRandom.cs b/src/NadekoBot/Common/NadekoRandom.cs index 7df59ed94..7094171b2 100644 --- a/src/NadekoBot/Common/NadekoRandom.cs +++ b/src/NadekoBot/Common/NadekoRandom.cs @@ -2,7 +2,7 @@ using System.Security.Cryptography; namespace NadekoBot.Common; -// todo minby maxby + public class NadekoRandom : Random { private readonly RandomNumberGenerator _rng; diff --git a/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs b/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs index b87924fb6..962cfc5ea 100644 --- a/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs +++ b/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs @@ -4,7 +4,7 @@ using NadekoBot.Db; namespace NadekoBot.Modules.Administration.Services; // todo dateonly timeonly -// todo new apis +// todo timer public class GameVoiceChannelService : INService { public ConcurrentHashSet GameVoiceChannels { get; } diff --git a/src/NadekoBot/Modules/Administration/PermOverrides/DiscordPermOverrideCommands.cs b/src/NadekoBot/Modules/Administration/PermOverrides/DiscordPermOverrideCommands.cs index 276e994e1..7e1b7ecfa 100644 --- a/src/NadekoBot/Modules/Administration/PermOverrides/DiscordPermOverrideCommands.cs +++ b/src/NadekoBot/Modules/Administration/PermOverrides/DiscordPermOverrideCommands.cs @@ -41,6 +41,7 @@ public partial class Administration if (!result) return; + await _service.ClearAllOverrides(ctx.Guild.Id); await ReplyConfirmLocalizedAsync(strs.perm_override_all); @@ -66,8 +67,8 @@ public partial class Administration if (thisPageOverrides.Count == 0) eb.WithDescription(GetText(strs.perm_override_page_none)); else - eb.WithDescription(string.Join("\n", - thisPageOverrides.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}"))); + eb.WithDescription(thisPageOverrides.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}") + .Join("\n")); return eb; }, diff --git a/src/NadekoBot/Modules/Administration/PlayingRotate/PlayingRotateService.cs b/src/NadekoBot/Modules/Administration/PlayingRotate/PlayingRotateService.cs index 1a6d6bd71..e47d2bba6 100644 --- a/src/NadekoBot/Modules/Administration/PlayingRotate/PlayingRotateService.cs +++ b/src/NadekoBot/Modules/Administration/PlayingRotate/PlayingRotateService.cs @@ -1,66 +1,64 @@ #nullable disable using Microsoft.EntityFrameworkCore; +using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Services.Database.Models; namespace NadekoBot.Modules.Administration.Services; -public sealed class PlayingRotateService : INService +public sealed class PlayingRotateService : INService, IReadyExecutor { - private readonly Timer _t; private readonly BotConfigService _bss; private readonly SelfService _selfService; private readonly Replacer _rep; private readonly DbService _db; - private readonly Bot _bot; public PlayingRotateService( DiscordSocketClient client, DbService db, - Bot bot, BotConfigService bss, IEnumerable phProviders, SelfService selfService) { _db = db; - _bot = bot; _bss = bss; _selfService = selfService; if (client.ShardId == 0) { _rep = new ReplacementBuilder().WithClient(client).WithProviders(phProviders).Build(); - - _t = new(RotatingStatuses, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); } } - private async void RotatingStatuses(object objState) + public async Task OnReadyAsync() { - try + var timer = new PeriodicTimer(TimeSpan.FromMinutes(1)); + var index = 0; + while (await timer.WaitForNextTickAsync()) { - var state = (TimerState)objState; - - if (!_bss.Data.RotateStatuses) return; - - IReadOnlyList rotatingStatuses; - await using (var uow = _db.GetDbContext()) + try { - rotatingStatuses = uow.RotatingStatus.AsNoTracking().OrderBy(x => x.Id).ToList(); + if (!_bss.Data.RotateStatuses) return; + + IReadOnlyList rotatingStatuses; + await using (var uow = _db.GetDbContext()) + { + rotatingStatuses = uow.RotatingStatus.AsNoTracking().OrderBy(x => x.Id).ToList(); + } + + if (rotatingStatuses.Count == 0) + return; + + var playingStatus = index >= rotatingStatuses.Count + ? rotatingStatuses[index = 0] + : rotatingStatuses[index++]; + + var statusText = _rep.Replace(playingStatus.Status); + await _selfService.SetGameAsync(statusText, playingStatus.Type); + } + catch (Exception ex) + { + Log.Warning(ex, "Rotating playing status errored: {ErrorMessage}", ex.Message); } - - if (rotatingStatuses.Count == 0) - return; - - var playingStatus = state.Index >= rotatingStatuses.Count - ? rotatingStatuses[state.Index = 0] - : rotatingStatuses[state.Index++]; - - var statusText = _rep.Replace(playingStatus.Status); - await _selfService.SetGameAsync(statusText, playingStatus.Type); - } - catch (Exception ex) - { - Log.Warning(ex, "Rotating playing status errored: {ErrorMessage}", ex.Message); } } @@ -100,9 +98,4 @@ public sealed class PlayingRotateService : INService using var uow = _db.GetDbContext(); return uow.RotatingStatus.AsNoTracking().ToList(); } - - private class TimerState - { - public int Index { get; set; } - } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Role/RoleCommands.cs b/src/NadekoBot/Modules/Administration/Role/RoleCommands.cs index 7bc846fc5..7624e3842 100644 --- a/src/NadekoBot/Modules/Administration/Role/RoleCommands.cs +++ b/src/NadekoBot/Modules/Administration/Role/RoleCommands.cs @@ -40,7 +40,9 @@ public partial class Administration } var role = (IRole)roleResult.BestMatch; - if (role.Position > ((IGuildUser)ctx.User).GetRoles().Select(r => r.Position).Max() + if (role.Position > ((IGuildUser)ctx.User).GetRoles() + .Select(r => r.Position) + .Max() && ctx.User.Id != ctx.Guild.OwnerId) return null; var emote = x.Last().ToIEmote(); diff --git a/src/NadekoBot/Modules/CustomReactions/NadekoExpressions.cs b/src/NadekoBot/Modules/CustomReactions/NadekoExpressions.cs index a9819660e..cf1d2301d 100644 --- a/src/NadekoBot/Modules/CustomReactions/NadekoExpressions.cs +++ b/src/NadekoBot/Modules/CustomReactions/NadekoExpressions.cs @@ -2,8 +2,6 @@ namespace NadekoBot.Modules.NadekoExpressions; -// todo string.join to .Join(", ") - [Name("Expressions")] public partial class NadekoExpressions : NadekoModule { diff --git a/src/NadekoBot/Modules/Gambling/~Shared/Deck.cs b/src/NadekoBot/Modules/Gambling/~Shared/Decks/Deck.cs similarity index 95% rename from src/NadekoBot/Modules/Gambling/~Shared/Deck.cs rename to src/NadekoBot/Modules/Gambling/~Shared/Decks/Deck.cs index c5c232345..3cd00c796 100644 --- a/src/NadekoBot/Modules/Gambling/~Shared/Deck.cs +++ b/src/NadekoBot/Modules/Gambling/~Shared/Decks/Deck.cs @@ -1,22 +1,6 @@ #nullable disable namespace NadekoBot.Modules.Gambling.Common; -public class QuadDeck : Deck -{ - protected override void RefillPool() - { - CardPool = new(52 * 4); - for (var j = 1; j < 14; j++) - for (var i = 1; i < 5; i++) - { - CardPool.Add(new((CardSuit)i, j)); - CardPool.Add(new((CardSuit)i, j)); - CardPool.Add(new((CardSuit)i, j)); - CardPool.Add(new((CardSuit)i, j)); - } - } -} - public class Deck { public enum CardSuit diff --git a/src/NadekoBot/Modules/Gambling/~Shared/Decks/QuadDeck.cs b/src/NadekoBot/Modules/Gambling/~Shared/Decks/QuadDeck.cs new file mode 100644 index 000000000..312d662fc --- /dev/null +++ b/src/NadekoBot/Modules/Gambling/~Shared/Decks/QuadDeck.cs @@ -0,0 +1,17 @@ +namespace NadekoBot.Modules.Gambling.Common; + +public class QuadDeck : Deck +{ + protected override void RefillPool() + { + CardPool = new(52 * 4); + for (var j = 1; j < 14; j++) + for (var i = 1; i < 5; i++) + { + CardPool.Add(new((CardSuit)i, j)); + CardPool.Add(new((CardSuit)i, j)); + CardPool.Add(new((CardSuit)i, j)); + CardPool.Add(new((CardSuit)i, j)); + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index cf15fa68a..f3bd85e3b 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -49,7 +49,6 @@ - diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 092121c2d..ffbbe4420 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -1,19 +1,9 @@ using Humanizer.Localisation; -using SixLabors.Fonts; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Drawing; -using SixLabors.ImageSharp.Drawing.Processing; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System.Globalization; using System.Net.Http.Headers; using System.Text.Json; using System.Text.RegularExpressions; -using Color = Discord.Color; -// todo imagesharp extensions namespace NadekoBot.Extensions; public static class Extensions @@ -65,45 +55,7 @@ public static class Extensions public static IEmote ToIEmote(this string emojiStr) => Emote.TryParse(emojiStr, out var maybeEmote) ? maybeEmote : new Emoji(emojiStr); - // https://github.com/SixLabors/Samples/blob/master/ImageSharp/AvatarWithRoundedCorner/Program.cs - public static IImageProcessingContext ApplyRoundedCorners(this IImageProcessingContext ctx, float cornerRadius) - { - 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 = ctx.Fill(SixLabors.ImageSharp.Color.Red, c); - - return ctx; - } - - private static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) - { - // first create a square - 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)); - - // corner is now a corner shape positions top left - //lets make 3 more positioned correctly, we can do that by translating the original around the center of the image - - var rightPos = imageWidth - cornerTopLeft.Bounds.Width + 1; - var bottomPos = imageHeight - cornerTopLeft.Bounds.Height + 1; - - // move it across the width of the image - the width of the shape - var cornerTopRight = cornerTopLeft.RotateDegree(90).Translate(rightPos, 0); - var cornerBottomLeft = cornerTopLeft.RotateDegree(-90).Translate(0, bottomPos); - var cornerBottomRight = cornerTopLeft.RotateDegree(180).Translate(rightPos, bottomPos); - - return new PathCollection(cornerTopLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); - } + /// /// First 10 characters of teh bot token. @@ -139,9 +91,6 @@ public static class Extensions return embed.WithFooter(curPage.ToString()); } - public static Color ToDiscordColor(this Rgba32 color) - => new(color.R, color.G, color.B); - public static IEmbedBuilder WithOkColor(this IEmbedBuilder eb) => eb.WithColor(EmbedColor.Ok); @@ -215,47 +164,6 @@ public static class Extensions public static string ToJson(this T any, JsonSerializerOptions? options = null) => JsonSerializer.Serialize(any, options); - /// - /// Adds fallback fonts to - /// - /// to which fallback fonts will be added to - /// List of fallback Font Families to add - /// The same to allow chaining - public static TextOptions WithFallbackFonts(this TextOptions opts, List fallback) - { - foreach (var ff in fallback) opts.FallbackFonts.Add(ff); - - return opts; - } - - /// - /// Adds fallback fonts to - /// - /// to which fallback fonts will be added to - /// List of fallback Font Families to add - /// The same to allow chaining - public static TextGraphicsOptions WithFallbackFonts(this TextGraphicsOptions opts, List fallback) - { - opts.TextOptions.WithFallbackFonts(fallback); - return opts; - } - - public static MemoryStream ToStream(this Image img, IImageFormat? format = null) - { - var imageStream = new MemoryStream(); - if (format?.Name == "GIF") - img.SaveAsGif(imageStream); - else - img.SaveAsPng(imageStream, - new() - { - ColorType = PngColorType.RgbWithAlpha, CompressionLevel = PngCompressionLevel.BestCompression - }); - - imageStream.Position = 0; - return imageStream; - } - public static Stream ToStream(this IEnumerable bytes, bool canWrite = false) { var ms = new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); diff --git a/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs index b76c4ee67..900c8b44f 100644 --- a/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs +++ b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs @@ -84,8 +84,9 @@ public static class MessageChannelExtensions Func howToPrint, int columns = 3) => ch.SendMessageAsync($@"{seed}```css -{string.Join("\n", items.Chunk(columns) - .Select(ig => string.Concat(ig.Select(howToPrint))))} +{items.Chunk(columns) + .Select(ig => string.Concat(ig.Select(howToPrint))) + .Join("\n")} ```"); public static Task SendTableAsync( diff --git a/src/NadekoBot/_Extensions/ImagesharpExtensions.cs b/src/NadekoBot/_Extensions/ImagesharpExtensions.cs new file mode 100644 index 000000000..6aa2edcce --- /dev/null +++ b/src/NadekoBot/_Extensions/ImagesharpExtensions.cs @@ -0,0 +1,98 @@ +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Color = Discord.Color; + +namespace NadekoBot.Extensions; + +public static class ImagesharpExtensions +{ + /// + /// Adds fallback fonts to + /// + /// to which fallback fonts will be added to + /// List of fallback Font Families to add + /// The same to allow chaining + public static TextOptions WithFallbackFonts(this TextOptions opts, List fallback) + { + foreach (var ff in fallback) opts.FallbackFonts.Add(ff); + + return opts; + } + + /// + /// Adds fallback fonts to + /// + /// to which fallback fonts will be added to + /// List of fallback Font Families to add + /// The same to allow chaining + public static TextGraphicsOptions WithFallbackFonts(this TextGraphicsOptions opts, List fallback) + { + opts.TextOptions.WithFallbackFonts(fallback); + return opts; + } + + // https://github.com/SixLabors/Samples/blob/master/ImageSharp/AvatarWithRoundedCorner/Program.cs + public static IImageProcessingContext ApplyRoundedCorners(this IImageProcessingContext ctx, float cornerRadius) + { + 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 = ctx.Fill(SixLabors.ImageSharp.Color.Red, c); + + return ctx; + } + + private static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) + { + // first create a square + 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)); + + // corner is now a corner shape positions top left + //lets make 3 more positioned correctly, we can do that by translating the original around the center of the image + + var rightPos = imageWidth - cornerTopLeft.Bounds.Width + 1; + var bottomPos = imageHeight - cornerTopLeft.Bounds.Height + 1; + + // move it across the width of the image - the width of the shape + var cornerTopRight = cornerTopLeft.RotateDegree(90).Translate(rightPos, 0); + var cornerBottomLeft = cornerTopLeft.RotateDegree(-90).Translate(0, bottomPos); + var cornerBottomRight = cornerTopLeft.RotateDegree(180).Translate(rightPos, bottomPos); + + return new PathCollection(cornerTopLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); + } + + public static Color ToDiscordColor(this Rgba32 color) + => new(color.R, color.G, color.B); + + public static MemoryStream ToStream(this Image img, IImageFormat? format = null) + { + var imageStream = new MemoryStream(); + if (format?.Name == "GIF") + img.SaveAsGif(imageStream); + else + img.SaveAsPng(imageStream, + new() + { + ColorType = PngColorType.RgbWithAlpha, CompressionLevel = PngCompressionLevel.BestCompression + }); + + imageStream.Position = 0; + return imageStream; + } +} \ No newline at end of file diff --git a/src/NadekoBot/_Extensions/StringExtensions.cs b/src/NadekoBot/_Extensions/StringExtensions.cs index cd7991bed..9d91a38bb 100644 --- a/src/NadekoBot/_Extensions/StringExtensions.cs +++ b/src/NadekoBot/_Extensions/StringExtensions.cs @@ -45,7 +45,7 @@ public static class StringExtensions tokens[i] = token[..1].ToUpperInvariant() + token[1..]; } - return string.Join(" ", tokens).Replace(" Of ", " of ").Replace(" The ", " the "); + return tokens.Join(" ").Replace(" Of ", " of ").Replace(" The ", " the "); } //http://www.dotnetperls.com/levenshtein @@ -124,7 +124,7 @@ public static class StringExtensions } public static string GetInitials(this string txt, string glue = "") - => string.Join(glue, txt.Split(' ').Select(x => x.FirstOrDefault())); + => txt.Split(' ').Select(x => x.FirstOrDefault()).Join(glue); public static bool IsAlphaNumeric(this string txt) => txt.All(c => _lettersAndDigits.Contains(c));