diff --git a/src/NadekoBot/.editorconfig b/src/NadekoBot/.editorconfig index 38173d65f..f38601851 100644 --- a/src/NadekoBot/.editorconfig +++ b/src/NadekoBot/.editorconfig @@ -173,6 +173,14 @@ csharp_preserve_single_line_statements = true # Naming rules +dotnet_naming_rule.private_readonly_field.symbols = private_readonly_field +dotnet_naming_rule.private_readonly_field.style = begins_with_underscore +dotnet_naming_rule.private_readonly_field.severity = warning + +dotnet_naming_rule.private_field.symbols = private_field +dotnet_naming_rule.private_field.style = camel_case +dotnet_naming_rule.private_field.severity = warning + dotnet_naming_rule.const_fields.symbols = const_fields dotnet_naming_rule.const_fields.style = all_upper dotnet_naming_rule.const_fields.severity = warning @@ -209,10 +217,6 @@ dotnet_naming_rule.async_method_should_be_ends_with_async.severity = error dotnet_naming_rule.async_method_should_be_ends_with_async.symbols = async_method dotnet_naming_rule.async_method_should_be_ends_with_async.style = ends_with_async -dotnet_naming_rule.private_field_should_be_begins_with_underscore.severity = error -dotnet_naming_rule.private_field_should_be_begins_with_underscore.symbols = private_field -dotnet_naming_rule.private_field_should_be_begins_with_underscore.style = begins_with_underscore - dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case @@ -221,10 +225,6 @@ dotnet_naming_rule.local_variable_should_be_camel_case.severity = error dotnet_naming_rule.local_variable_should_be_camel_case.symbols = local_variable dotnet_naming_rule.local_variable_should_be_camel_case.style = camel_case -dotnet_naming_rule.public_anything_should_be_pascal_case.severity = error -dotnet_naming_rule.public_anything_should_be_pascal_case.symbols = public_anything -dotnet_naming_rule.public_anything_should_be_pascal_case.style = pascal_case - # Symbol specifications dotnet_naming_symbols.const_fields.required_modifiers = const @@ -262,8 +262,12 @@ dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, meth dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.private_readonly_field.applicable_kinds = field +dotnet_naming_symbols.private_readonly_field.applicable_accessibilities = private +dotnet_naming_symbols.private_readonly_field.required_modifiers = readonly + dotnet_naming_symbols.private_field.applicable_kinds = field -dotnet_naming_symbols.private_field.applicable_accessibilities = private +dotnet_naming_symbols.private_field.applicable_accessibilities = private, protected dotnet_naming_symbols.private_field.required_modifiers = dotnet_naming_symbols.async_method.applicable_kinds = method, local_function @@ -274,10 +278,6 @@ dotnet_naming_symbols.local_variable.applicable_kinds = parameter, local dotnet_naming_symbols.local_variable.applicable_accessibilities = local dotnet_naming_symbols.local_variable.required_modifiers = -dotnet_naming_symbols.public_anything.applicable_kinds = property, field, event, class, struct, interface, enum, delegate, method -dotnet_naming_symbols.public_anything.applicable_accessibilities = public, internal -dotnet_naming_symbols.public_anything.required_modifiers = - # Naming styles @@ -330,8 +330,11 @@ resharper_csharp_wrap_parameters_style = chop_if_long resharper_force_chop_compound_if_expression = true resharper_keep_existing_linebreaks = false resharper_max_formal_parameters_on_line = 3 +resharper_wrap_chained_binary_expressions = chop_if_long +resharper_wrap_chained_binary_patterns = chop_if_long +resharper_wrap_chained_method_calls = chop_always -resharper_csharp_wrap_before_first_type_parameter_constraint=true -resharper_csharp_place_type_constraints_on_same_line=false -resharper_csharp_wrap_before_extends_colon=true -resharper_csharp_place_constructor_initializer_on_same_line=false \ No newline at end of file +resharper_csharp_wrap_before_first_type_parameter_constraint = true +resharper_csharp_place_type_constraints_on_same_line = false +resharper_csharp_wrap_before_extends_colon = true +resharper_csharp_place_constructor_initializer_on_same_line = false diff --git a/src/NadekoBot/Common/TypeReaders/AddRemove.cs b/src/NadekoBot/Common/AddRemove.cs similarity index 62% rename from src/NadekoBot/Common/TypeReaders/AddRemove.cs rename to src/NadekoBot/Common/AddRemove.cs index 3f6fdc62b..5578cb253 100644 --- a/src/NadekoBot/Common/TypeReaders/AddRemove.cs +++ b/src/NadekoBot/Common/AddRemove.cs @@ -1,8 +1,9 @@ -namespace NadekoBot.Common.TypeReaders; +namespace NadekoBot.Common; public enum AddRemove { Add = int.MinValue, + Remove = int.MinValue + 1, Rem = int.MinValue + 1, Rm = int.MinValue + 1, } \ No newline at end of file diff --git a/src/NadekoBot/Common/AsyncLazy.cs b/src/NadekoBot/Common/AsyncLazy.cs index 3b867e974..e155b4202 100644 --- a/src/NadekoBot/Common/AsyncLazy.cs +++ b/src/NadekoBot/Common/AsyncLazy.cs @@ -4,13 +4,15 @@ namespace NadekoBot.Common; public class AsyncLazy : Lazy> { - public AsyncLazy(Func valueFactory) : - base(() => Task.Run(valueFactory)) - { } + public AsyncLazy(Func valueFactory) + : base(() => Task.Run(valueFactory)) + { + } - public AsyncLazy(Func> taskFactory) : - base(() => Task.Run(taskFactory)) - { } + public AsyncLazy(Func> taskFactory) + : base(() => Task.Run(taskFactory)) + { + } public TaskAwaiter GetAwaiter() => Value.GetAwaiter(); diff --git a/src/NadekoBot/Common/CmdStrings.cs b/src/NadekoBot/Common/CmdStrings.cs index 4c05fe309..1d81823ac 100644 --- a/src/NadekoBot/Common/CmdStrings.cs +++ b/src/NadekoBot/Common/CmdStrings.cs @@ -8,10 +8,7 @@ public class CmdStrings public string Description { get; } [JsonConstructor] - public CmdStrings( - [JsonProperty("args")]string[] usages, - [JsonProperty("desc")]string description - ) + public CmdStrings([JsonProperty("args")] string[] usages, [JsonProperty("desc")] string description) { Usages = usages; Description = description; diff --git a/src/NadekoBot/Common/Collections/IndexedCollection.cs b/src/NadekoBot/Common/Collections/IndexedCollection.cs index def4639b9..ae26a43be 100644 --- a/src/NadekoBot/Common/Collections/IndexedCollection.cs +++ b/src/NadekoBot/Common/Collections/IndexedCollection.cs @@ -3,14 +3,20 @@ using NadekoBot.Services.Database.Models; namespace NadekoBot.Common.Collections; -public class IndexedCollection : IList where T : class, IIndexed +public class IndexedCollection : IList + where T : class, IIndexed { public List Source { get; } private readonly object _locker = new(); - public int Count => Source.Count; - public bool IsReadOnly => false; - public int IndexOf(T item) => item.Index; + public int Count + => Source.Count; + + public bool IsReadOnly + => false; + + public int IndexOf([NotNull] T item) + => item.Index; public IndexedCollection() => Source = new(); @@ -36,16 +42,17 @@ public class IndexedCollection : IList where T : class, IIndexed } } - public static implicit operator List(IndexedCollection x) => - x.Source; + public static implicit operator List(IndexedCollection x) + => x.Source; - public List ToList() => Source.ToList(); + public List ToList() + => Source.ToList(); - public IEnumerator GetEnumerator() => - Source.GetEnumerator(); + public IEnumerator GetEnumerator() + => Source.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => - Source.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() + => Source.GetEnumerator(); public void Add(T item) { @@ -82,19 +89,21 @@ public class IndexedCollection : IList where T : class, IIndexed public virtual bool Remove(T item) { - bool removed; lock (_locker) { - if (removed = Source.Remove(item)) + if (Source.Remove(item)) { for (var i = 0; i < Source.Count; i++) { if (Source[i].Index != i) Source[i].Index = i; } + + return true; } } - return removed; + + return false; } public virtual void Insert(int index, T item) diff --git a/src/NadekoBot/Common/Configs/BotConfig.cs b/src/NadekoBot/Common/Configs/BotConfig.cs index 1f2cab7ce..53f208132 100644 --- a/src/NadekoBot/Common/Configs/BotConfig.cs +++ b/src/NadekoBot/Common/Configs/BotConfig.cs @@ -18,14 +18,16 @@ next to the response. The color depends whether the command is completed, errored or in progress (pending) Color settings below are for the color of those lines. To get color's hex, you can go here https://htmlcolorcodes.com/ -and copy the hex code fo your selected color (marked as #)")] +and copy the hex code fo your selected color (marked as #)" + )] public ColorConfig Color { get; set; } - + [Comment("Default bot language. It has to be in the list of supported languages (.langli)")] public CultureInfo DefaultLocale { get; set; } - + [Comment(@"Style in which executed commands will show up in the console. -Allowed values: Simple, Normal, None")] +Allowed values: Simple, Normal, None" + )] public ConsoleOutputType ConsoleOutputType { get; set; } // [Comment(@"For what kind of updates will the bot check. @@ -38,30 +40,35 @@ Allowed values: Simple, Normal, None")] [Comment(@"Do you want any messages sent by users in Bot's DM to be forwarded to the owner(s)?")] public bool ForwardMessages { get; set; } - [Comment(@"Do you want the message to be forwarded only to the first owner specified in the list of owners (in creds.yml), -or all owners? (this might cause the bot to lag if there's a lot of owners specified)")] + [Comment( + @"Do you want the message to be forwarded only to the first owner specified in the list of owners (in creds.yml), +or all owners? (this might cause the bot to lag if there's a lot of owners specified)" + )] public bool ForwardToAllOwners { get; set; } [Comment(@"When a user DMs the bot with a message which is not a command they will receive this message. Leave empty for no response. The string which will be sent whenever someone DMs the bot. -Supports embeds. How it looks: https://puu.sh/B0BLV.png")] +Supports embeds. How it looks: https://puu.sh/B0BLV.png" + )] [YamlMember(ScalarStyle = ScalarStyle.Literal)] public string DmHelpText { get; set; } - + [Comment(@"Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response. Case insensitive. -Leave empty to reply with DmHelpText to every DM.")] +Leave empty to reply with DmHelpText to every DM." + )] public List DmHelpTextKeywords { get; set; } [Comment(@"This is the response for the .h command")] [YamlMember(ScalarStyle = ScalarStyle.Literal)] public string HelpText { get; set; } + [Comment(@"List of modules and commands completely blocked on the bot")] public BlockedConfig Blocked { get; set; } [Comment(@"Which string will be used to recognize the commands")] public string Prefix { get; set; } - + [Comment(@"Toggles whether your bot will group greet/bye messages into a single message every 5 seconds. 1st user who joins will get greeted immediately If more users join within the next 5 seconds, they will be greeted in groups of 5. @@ -70,12 +77,14 @@ Keep in mind this might break some of your embeds - for example if you have %use it will become invalid, as it will resolve to a list of avatars of grouped users. note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited, - and (slightly) reduce the greet spam in those servers.")] + and (slightly) reduce the greet spam in those servers." + )] public bool GroupGreets { get; set; } - + [Comment(@"Whether the bot will rotate through all specified statuses. This setting can be changed via .rots command. -See RotatingStatuses submodule in Administration.")] +See RotatingStatuses submodule in Administration." + )] public bool RotateStatuses { get; set; } // [Comment(@"Whether the prefix will be a suffix, or prefix. @@ -159,10 +168,10 @@ public partial class ColorConfig { [Comment(@"Color used for embed responses when command successfully executes")] public Rgba32 Ok { get; set; } - + [Comment(@"Color used for embed responses when command has an error")] public Rgba32 Error { get; set; } - + [Comment(@"Color used for embed responses while command is doing work or is in progress")] public Rgba32 Pending { get; set; } @@ -173,7 +182,7 @@ public partial class ColorConfig Pending = Rgba32.ParseHex("faa61a"); } } - + public enum ConsoleOutputType { Normal = 0, diff --git a/src/NadekoBot/Common/Creds.cs b/src/NadekoBot/Common/Creds.cs index e1f9d782c..fa0c4d46d 100644 --- a/src/NadekoBot/Common/Creds.cs +++ b/src/NadekoBot/Common/Creds.cs @@ -11,81 +11,93 @@ public sealed class Creds : IBotCredentials OwnerIds = new List(); TotalShards = 1; GoogleApiKey = string.Empty; - Votes = new(string.Empty, string.Empty, string.Empty, string.Empty); - Patreon = new(string.Empty, string.Empty, string.Empty, string.Empty); + Votes = new(string.Empty, + string.Empty, + string.Empty, + string.Empty + ); + Patreon = new(string.Empty, + string.Empty, + string.Empty, + string.Empty + ); BotListToken = string.Empty; CleverbotApiKey = string.Empty; RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password="; - Db = new() - { - Type = "sqlite", - ConnectionString = "Data Source=data/NadekoBot.db" - }; + Db = new() { Type = "sqlite", ConnectionString = "Data Source=data/NadekoBot.db" }; CoordinatorUrl = "http://localhost:3442"; - RestartCommand = new() - { - }; + RestartCommand = new(); } [Comment(@"DO NOT CHANGE")] public int Version { get; set; } - + [Comment(@"Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/")] public string Token { get; set; } [Comment(@"List of Ids of the users who have bot owner permissions -**DO NOT ADD PEOPLE YOU DON'T TRUST**")] +**DO NOT ADD PEOPLE YOU DON'T TRUST**" + )] public ICollection OwnerIds { get; set; } - + [Comment(@"The number of shards that the bot will running on. -Leave at 1 if you don't know what you're doing.")] +Leave at 1 if you don't know what you're doing." + )] public int TotalShards { get; set; } - - [Comment(@"Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it. + + [Comment( + @"Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it. Then, go to APIs and Services -> Credentials and click Create credentials -> API key. -Used only for Youtube Data Api (at the moment).")] +Used only for Youtube Data Api (at the moment)." + )] public string GoogleApiKey { get; set; } - + [Comment(@"Settings for voting system for discordbots. Meant for use on global Nadeko.")] public VotesSettings Votes { get; set; } - + [Comment(@"Patreon auto reward system settings. -go to https://www.patreon.com/portal -> my clients -> create client")] +go to https://www.patreon.com/portal -> my clients -> create client" + )] public PatreonSettings Patreon { get; set; } - + [Comment(@"Api key for sending stats to DiscordBotList.")] public string BotListToken { get; set; } - + [Comment(@"Official cleverbot api key.")] public string CleverbotApiKey { get; set; } - + [Comment(@"Redis connection string. Don't change if you don't know what you're doing.")] public string RedisOptions { get; set; } - + [Comment(@"Database options. Don't change if you don't know what you're doing. Leave null for default values")] public DbOptions Db { get; set; } [Comment(@"Address and port of the coordinator endpoint. Leave empty for default. -Change only if you've changed the coordinator address or port.")] +Change only if you've changed the coordinator address or port." + )] public string CoordinatorUrl { get; set; } - - [Comment(@"Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)")] + + [Comment(@"Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)" + )] public string RapidApiKey { get; set; } [Comment(@"https://locationiq.com api key (register and you will receive the token in the email). -Used only for .time command.")] +Used only for .time command." + )] public string LocationIqApiKey { get; set; } [Comment(@"https://timezonedb.com api key (register and you will receive the token in the email). -Used only for .time command")] +Used only for .time command" + )] public string TimezoneDbApiKey { get; set; } [Comment(@"https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use. -Used for cryptocurrency related commands.")] +Used for cryptocurrency related commands." + )] public string CoinmarketcapApiKey { get; set; } - + [Comment(@"Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api")] public string OsuApiKey { get; set; } @@ -99,7 +111,8 @@ Linux default args: ""NadekoBot.dll -- {0}"" Windows default cmd: NadekoBot.exe - args: {0}")] + args: {0}" + )] public RestartConfig RestartCommand { get; set; } @@ -107,6 +120,7 @@ Windows default { [Comment(@"Database type. Only sqlite supported atm")] public string Type { get; set; } + [Comment(@"Connection string. Will default to ""Data Source=data/NadekoBot.db""")] public string ConnectionString { get; set; } } @@ -118,10 +132,16 @@ Windows default public string RefreshToken { get; set; } public string ClientSecret { get; set; } - [Comment(@"Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type ""prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);"" in the console. (ctrl + shift + i)")] + [Comment( + @"Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type ""prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);"" in the console. (ctrl + shift + i)" + )] public string CampaignId { get; set; } - public PatreonSettings(string accessToken, string refreshToken, string clientSecret, string campaignId) + public PatreonSettings( + string accessToken, + string refreshToken, + string clientSecret, + string campaignId) { AccessToken = accessToken; RefreshToken = refreshToken; @@ -131,7 +151,6 @@ Windows default public PatreonSettings() { - } } @@ -139,28 +158,35 @@ Windows default { [Comment(@"top.gg votes service url This is the url of your instance of the NadekoBot.Votes api -Example: https://votes.my.cool.bot.com")] +Example: https://votes.my.cool.bot.com" + )] public string TopggServiceUrl { get; set; } - + [Comment(@"Authorization header value sent to the TopGG service url with each request -This should be equivalent to the TopggKey in your NadekoBot.Votes api appsettings.json file")] +This should be equivalent to the TopggKey in your NadekoBot.Votes api appsettings.json file" + )] public string TopggKey { get; set; } - + [Comment(@"discords.com votes service url This is the url of your instance of the NadekoBot.Votes api -Example: https://votes.my.cool.bot.com")] +Example: https://votes.my.cool.bot.com" + )] public string DiscordsServiceUrl { get; set; } - + [Comment(@"Authorization header value sent to the Discords service url with each request -This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsettings.json file")] +This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsettings.json file" + )] public string DiscordsKey { get; set; } public VotesSettings() { - } - - public VotesSettings(string topggServiceUrl, string topggKey, string discordsServiceUrl, string discordsKey) + + public VotesSettings( + string topggServiceUrl, + string topggKey, + string discordsServiceUrl, + string discordsKey) { TopggServiceUrl = topggServiceUrl; TopggKey = topggKey; diff --git a/src/NadekoBot/Common/DownloadTracker.cs b/src/NadekoBot/Common/DownloadTracker.cs index 22bdb301b..5c6afc06e 100644 --- a/src/NadekoBot/Common/DownloadTracker.cs +++ b/src/NadekoBot/Common/DownloadTracker.cs @@ -3,7 +3,7 @@ public class DownloadTracker : INService { private ConcurrentDictionary LastDownloads { get; } = new(); - private readonly SemaphoreSlim downloadUsersSemaphore = new(1, 1); + private readonly SemaphoreSlim _downloadUsersSemaphore = new(1, 1); /// /// Ensures all users on the specified guild were downloaded within the last hour. @@ -12,16 +12,16 @@ public class DownloadTracker : INService /// Task representing download state public async Task EnsureUsersDownloadedAsync(IGuild guild) { - await downloadUsersSemaphore.WaitAsync(); + await _downloadUsersSemaphore.WaitAsync(); try { var now = DateTime.UtcNow; // download once per hour at most - var added = LastDownloads.AddOrUpdate( - guild.Id, + var added = LastDownloads.AddOrUpdate(guild.Id, now, - (key, old) => now - old > TimeSpan.FromHours(1) ? now : old); + (_, old) => now - old > TimeSpan.FromHours(1) ? now : old + ); // means that this entry was just added - download the users if (added == now) @@ -29,7 +29,7 @@ public class DownloadTracker : INService } finally { - downloadUsersSemaphore.Release(); + _downloadUsersSemaphore.Release(); } } } \ No newline at end of file diff --git a/src/NadekoBot/Common/Helpers.cs b/src/NadekoBot/Common/Helpers.cs index 35c455584..9b7d9fbbf 100644 --- a/src/NadekoBot/Common/Helpers.cs +++ b/src/NadekoBot/Common/Helpers.cs @@ -6,7 +6,7 @@ public static class Helpers { if (!Console.IsInputRedirected) Console.ReadKey(); - + Environment.Exit(exitCode); } } \ No newline at end of file diff --git a/src/NadekoBot/Common/IBotCredentials.cs b/src/NadekoBot/Common/IBotCredentials.cs index de1106b78..9dea7994f 100644 --- a/src/NadekoBot/Common/IBotCredentials.cs +++ b/src/NadekoBot/Common/IBotCredentials.cs @@ -21,7 +21,7 @@ public interface IBotCredentials string CoinmarketcapApiKey { get; } string CoordinatorUrl { get; set; } } - + public class RestartConfig { public string Cmd { get; set; } diff --git a/src/NadekoBot/Common/ICloneable.cs b/src/NadekoBot/Common/ICloneable.cs index deddec15e..59782c22e 100644 --- a/src/NadekoBot/Common/ICloneable.cs +++ b/src/NadekoBot/Common/ICloneable.cs @@ -1,6 +1,7 @@ namespace NadekoBot.Common; -public interface ICloneable where T : new() +public interface ICloneable + where T : new() { public T Clone(); } \ No newline at end of file diff --git a/src/NadekoBot/Common/JsonConverters/Rbga32Converter.cs b/src/NadekoBot/Common/JsonConverters/CultureInfoConverter.cs similarity index 51% rename from src/NadekoBot/Common/JsonConverters/Rbga32Converter.cs rename to src/NadekoBot/Common/JsonConverters/CultureInfoConverter.cs index 9a6b54e89..0d23c338b 100644 --- a/src/NadekoBot/Common/JsonConverters/Rbga32Converter.cs +++ b/src/NadekoBot/Common/JsonConverters/CultureInfoConverter.cs @@ -1,23 +1,13 @@ using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; -using SixLabors.ImageSharp.PixelFormats; namespace NadekoBot.Common.JsonConverters; -public class Rgba32Converter : JsonConverter -{ - public override Rgba32 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => Rgba32.ParseHex(reader.GetString()); - - public override void Write(Utf8JsonWriter writer, Rgba32 value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToHex()); -} - public class CultureInfoConverter : JsonConverter { public override CultureInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => new(reader.GetString()); + => new(reader.GetString() ?? "en-US"); public override void Write(Utf8JsonWriter writer, CultureInfo value, JsonSerializerOptions options) => writer.WriteStringValue(value.Name); diff --git a/src/NadekoBot/Common/JsonConverters/Rgba32Converter.cs b/src/NadekoBot/Common/JsonConverters/Rgba32Converter.cs new file mode 100644 index 000000000..eba8dbcf2 --- /dev/null +++ b/src/NadekoBot/Common/JsonConverters/Rgba32Converter.cs @@ -0,0 +1,14 @@ +using SixLabors.ImageSharp.PixelFormats; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NadekoBot.Common.JsonConverters; + +public class Rgba32Converter : JsonConverter +{ + public override Rgba32 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => Rgba32.ParseHex(reader.GetString()); + + public override void Write(Utf8JsonWriter writer, Rgba32 value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToHex()); +} \ No newline at end of file diff --git a/src/NadekoBot/Common/Kwum.cs b/src/NadekoBot/Common/Kwum.cs index d381d6f43..4174be695 100644 --- a/src/NadekoBot/Common/Kwum.cs +++ b/src/NadekoBot/Common/Kwum.cs @@ -4,14 +4,17 @@ namespace NadekoBot.Common; // needs proper invalid input check (character array input out of range) // needs negative number support +// ReSharper disable once InconsistentNaming +#pragma warning disable IDE1006 public readonly struct kwum : IEquatable +#pragma warning restore IDE1006 { private readonly int _value; - private const string ValidCharacters = "23456789abcdefghijkmnpqrstuvwxyz"; + private const string VALID_CHARACTERS = "23456789abcdefghijkmnpqrstuvwxyz"; public kwum(int num) => _value = num; - + public kwum(in char c) { if (!IsValidChar(c)) @@ -21,11 +24,11 @@ public readonly struct kwum : IEquatable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int InternalCharToValue(in char c) - => ValidCharacters.IndexOf(c); + private static int InternalCharToValue(in char c) + => VALID_CHARACTERS.IndexOf(c); public kwum(in ReadOnlySpan input) - {; + { _value = 0; for (var index = 0; index < input.Length; index++) { @@ -33,14 +36,14 @@ public readonly struct kwum : IEquatable if (!IsValidChar(c)) throw new ArgumentException("All characters need to be a valid kwum characters.", nameof(input)); - _value += ValidCharacters.IndexOf(c) * (int)Math.Pow(ValidCharacters.Length, input.Length - index - 1); + _value += VALID_CHARACTERS.IndexOf(c) * (int)Math.Pow(VALID_CHARACTERS.Length, input.Length - index - 1); } } public static bool TryParse(in ReadOnlySpan input, out kwum value) { value = default; - foreach(var c in input) + foreach (var c in input) if (!IsValidChar(c)) return false; @@ -59,25 +62,26 @@ public readonly struct kwum : IEquatable public static implicit operator long(kwum kwum) => kwum._value; - + public static implicit operator int(kwum kwum) => kwum._value; + public static implicit operator kwum(int num) => new(num); public static bool IsValidChar(char c) - => ValidCharacters.Contains(c); + => VALID_CHARACTERS.Contains(c); public override string ToString() { - var count = ValidCharacters.Length; + var count = VALID_CHARACTERS.Length; var localValue = _value; var arrSize = (int)Math.Log(localValue, count) + 1; Span chars = new char[arrSize]; while (localValue > 0) { localValue = Math.DivRem(localValue, count, out var rem); - chars[--arrSize] = ValidCharacters[rem]; + chars[--arrSize] = VALID_CHARACTERS[rem]; } return new(chars); diff --git a/src/NadekoBot/Common/LbOpts.cs b/src/NadekoBot/Common/LbOpts.cs index ec5d13a4f..58a624412 100644 --- a/src/NadekoBot/Common/LbOpts.cs +++ b/src/NadekoBot/Common/LbOpts.cs @@ -4,10 +4,14 @@ namespace NadekoBot.Common; public class LbOpts : INadekoCommandOptions { - [Option('c', "clean", Default = false, HelpText = "Only show users who are on the server.")] + [Option('c', + "clean", + Default = false, + HelpText = "Only show users who are on the server." + )] public bool Clean { get; set; } + public void NormalizeOptions() { - } } \ No newline at end of file diff --git a/src/NadekoBot/Common/LoginErrorHandler.cs b/src/NadekoBot/Common/LoginErrorHandler.cs index 7cdaa1d59..290d9b961 100644 --- a/src/NadekoBot/Common/LoginErrorHandler.cs +++ b/src/NadekoBot/Common/LoginErrorHandler.cs @@ -17,18 +17,21 @@ public class LoginErrorHandler case HttpStatusCode.Unauthorized: Log.Error("Your bot token is wrong.\n" + "You can find the bot token under the Bot tab in the developer page.\n" + - "Fix your token in the credentials file and restart the bot"); + "Fix your token in the credentials file and restart the bot" + ); break; case HttpStatusCode.BadRequest: Log.Error("Something has been incorrectly formatted in your credentials file.\n" + - "Use the JSON Guide as reference to fix it and restart the bot."); + "Use the JSON Guide as reference to fix it and restart the bot" + ); Log.Error("If you are on Linux, make sure Redis is installed and running"); break; case HttpStatusCode.RequestTimeout: Log.Error("The request timed out. Make sure you have no external program blocking the bot " + - "from connecting to the internet"); + "from connecting to the internet" + ); break; case HttpStatusCode.ServiceUnavailable: @@ -38,7 +41,8 @@ public class LoginErrorHandler case HttpStatusCode.TooManyRequests: Log.Error("Your bot has been ratelimited by Discord. Please, try again later.\n" + - "Global ratelimits usually last for an hour"); + "Global ratelimits usually last for an hour" + ); break; default: @@ -46,6 +50,6 @@ public class LoginErrorHandler break; } - Log.Fatal(ex.ToString()); + Log.Fatal(ex, "Fatal error occurred while loading credentials"); } } \ No newline at end of file diff --git a/src/NadekoBot/Common/ModuleBehaviors/IINputTransformer.cs b/src/NadekoBot/Common/ModuleBehaviors/IINputTransformer.cs deleted file mode 100644 index 510c76b1a..000000000 --- a/src/NadekoBot/Common/ModuleBehaviors/IINputTransformer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NadekoBot.Common.ModuleBehaviors; - -public interface IInputTransformer -{ - Task TransformInput(IGuild guild, IMessageChannel channel, IUser user, string input); -} \ No newline at end of file diff --git a/src/NadekoBot/Common/ModuleBehaviors/IInputTransformer.cs b/src/NadekoBot/Common/ModuleBehaviors/IInputTransformer.cs new file mode 100644 index 000000000..fedd67c85 --- /dev/null +++ b/src/NadekoBot/Common/ModuleBehaviors/IInputTransformer.cs @@ -0,0 +1,10 @@ +namespace NadekoBot.Common.ModuleBehaviors; + +public interface IInputTransformer +{ + Task TransformInput( + IGuild guild, + IMessageChannel channel, + IUser user, + string input); +} \ No newline at end of file diff --git a/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs b/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs index 516335a5c..d6c5456fd 100644 --- a/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs +++ b/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs @@ -3,6 +3,6 @@ public interface ILateBlocker { public int Priority { get; } - + Task TryBlockLate(ICommandContext context, string moduleName, CommandInfo command); } \ No newline at end of file diff --git a/src/NadekoBot/Common/NadekoModule.cs b/src/NadekoBot/Common/NadekoModule.cs index fbf6617d7..e7ce08211 100644 --- a/src/NadekoBot/Common/NadekoModule.cs +++ b/src/NadekoBot/Common/NadekoModule.cs @@ -1,60 +1,78 @@ using System.Globalization; +// ReSharper disable InconsistentNaming namespace NadekoBot.Modules; -[UsedImplicitly(ImplicitUseTargetFlags.Default - | ImplicitUseTargetFlags.WithInheritors - | ImplicitUseTargetFlags.WithMembers)] +[UsedImplicitly(ImplicitUseTargetFlags.Default | + ImplicitUseTargetFlags.WithInheritors | + ImplicitUseTargetFlags.WithMembers +)] public abstract class NadekoModule : ModuleBase { - protected CultureInfo _cultureInfo { get; set; } + protected CultureInfo Culture { get; set; } public IBotStrings Strings { get; set; } public CommandHandler CmdHandler { get; set; } public ILocalization Localization { get; set; } public IEmbedBuilderService _eb { get; set; } - public string Prefix => CmdHandler.GetPrefix(ctx.Guild); + public string Prefix + => CmdHandler.GetPrefix(ctx.Guild); - protected ICommandContext ctx => Context; - - protected NadekoModule() - { - } + protected ICommandContext ctx + => Context; protected override void BeforeExecute(CommandInfo cmd) - => _cultureInfo = Localization.GetCultureInfo(ctx.Guild?.Id); + => Culture = Localization.GetCultureInfo(ctx.Guild?.Id); - protected string GetText(in LocStr data) => - Strings.GetText(data, _cultureInfo); + protected string GetText(in LocStr data) + => Strings.GetText(data, Culture); public Task SendErrorAsync(string error) => ctx.Channel.SendErrorAsync(_eb, error); - - public Task SendErrorAsync(string title, string error, string url = null, string footer = null) - => ctx.Channel.SendErrorAsync(_eb, title, error, url, footer); - + + public Task SendErrorAsync( + string title, + string error, + string url = null, + string footer = null) + => ctx.Channel.SendErrorAsync(_eb, + title, + error, + url, + footer + ); + public Task SendConfirmAsync(string text) => ctx.Channel.SendConfirmAsync(_eb, text); - - public Task SendConfirmAsync(string title, string text, string url = null, string footer = null) - => ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer); - + + public Task SendConfirmAsync( + string title, + string text, + string url = null, + string footer = null) + => ctx.Channel.SendConfirmAsync(_eb, + title, + text, + url, + footer + ); + public Task SendPendingAsync(string text) => ctx.Channel.SendPendingAsync(_eb, text); - - public Task ErrorLocalizedAsync(LocStr str) + + public Task ErrorLocalizedAsync(LocStr str) => SendErrorAsync(GetText(str)); - public Task PendingLocalizedAsync(LocStr str) + public Task PendingLocalizedAsync(LocStr str) => SendPendingAsync(GetText(str)); - - public Task ConfirmLocalizedAsync(LocStr str) + + public Task ConfirmLocalizedAsync(LocStr str) => SendConfirmAsync(GetText(str)); - public Task ReplyErrorLocalizedAsync(LocStr str) + public Task ReplyErrorLocalizedAsync(LocStr str) => SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}"); - public Task ReplyPendingLocalizedAsync(LocStr str) + public Task ReplyPendingLocalizedAsync(LocStr str) => SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}"); public Task ReplyConfirmLocalizedAsync(LocStr str) @@ -62,17 +80,19 @@ public abstract class NadekoModule : ModuleBase public async Task PromptUserConfirmAsync(IEmbedBuilder embed) { - embed - .WithPendingColor() + embed.WithPendingColor() .WithFooter("yes/no"); - var msg = await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false); + var msg = await ctx.Channel.EmbedAsync(embed) + .ConfigureAwait(false); try { - var input = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id).ConfigureAwait(false); + var input = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id) + .ConfigureAwait(false); input = input?.ToUpperInvariant(); - if (input != "YES" && input != "Y") + if (input != "YES" && + input != "Y") { return false; } @@ -94,7 +114,9 @@ public abstract class NadekoModule : ModuleBase { dsc.MessageReceived += MessageReceived; - if (await Task.WhenAny(userInputTask.Task, Task.Delay(10000)).ConfigureAwait(false) != userInputTask.Task) + if (await Task.WhenAny(userInputTask.Task, Task.Delay(10000)) + .ConfigureAwait(false) != + userInputTask.Task) { return null; } @@ -109,21 +131,23 @@ public abstract class NadekoModule : ModuleBase Task MessageReceived(SocketMessage arg) { var _ = Task.Run(() => - { - if (arg is not SocketUserMessage userMsg || - userMsg.Channel is not ITextChannel chan || - userMsg.Author.Id != userId || - userMsg.Channel.Id != channelId) { + if (arg is not SocketUserMessage userMsg || + userMsg.Channel is not ITextChannel || + userMsg.Author.Id != userId || + userMsg.Channel.Id != channelId) + { + return Task.CompletedTask; + } + + if (userInputTask.TrySetResult(arg.Content)) + { + userMsg.DeleteAfter(1); + } + return Task.CompletedTask; } - - if (userInputTask.TrySetResult(arg.Content)) - { - userMsg.DeleteAfter(1); - } - return Task.CompletedTask; - }); + ); return Task.CompletedTask; } } @@ -132,20 +156,12 @@ public abstract class NadekoModule : ModuleBase public abstract class NadekoModule : NadekoModule { public TService _service { get; set; } - - protected NadekoModule() : base() - { - } } public abstract class NadekoSubmodule : NadekoModule { - protected NadekoSubmodule() : base() { } } public abstract class NadekoSubmodule : NadekoModule { - protected NadekoSubmodule() : base() - { - } } \ No newline at end of file diff --git a/src/NadekoBot/Common/NadekoModuleExtensions.cs b/src/NadekoBot/Common/NadekoModuleExtensions.cs deleted file mode 100644 index ceba46da0..000000000 --- a/src/NadekoBot/Common/NadekoModuleExtensions.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NadekoBot.Modules; - -public static class NadekoModuleExtensions -{ - -} \ No newline at end of file diff --git a/src/NadekoBot/Common/NadekoRandom.cs b/src/NadekoBot/Common/NadekoRandom.cs index 73e3034a2..7edb67683 100644 --- a/src/NadekoBot/Common/NadekoRandom.cs +++ b/src/NadekoBot/Common/NadekoRandom.cs @@ -6,7 +6,8 @@ public class NadekoRandom : Random { private readonly RandomNumberGenerator _rng; - public NadekoRandom() : base() + public NadekoRandom() + : base() => _rng = RandomNumberGenerator.Create(); public override int Next() diff --git a/src/NadekoBot/Common/NoPublicBotPrecondition.cs b/src/NadekoBot/Common/NoPublicBotPrecondition.cs index 855f02e2a..8a6ca8d93 100644 --- a/src/NadekoBot/Common/NoPublicBotPrecondition.cs +++ b/src/NadekoBot/Common/NoPublicBotPrecondition.cs @@ -1,12 +1,15 @@ -namespace NadekoBot.Common; +using System.Diagnostics.CodeAnalysis; + +namespace NadekoBot.Common; [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +[SuppressMessage("Style", "IDE0022:Use expression body for methods")] public sealed class NoPublicBotAttribute : PreconditionAttribute { public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) { #if GLOBAL_NADEKO - return Task.FromResult(PreconditionResult.FromError("Not available on the public bot. To learn how to selfhost a private bot, click [here](https://nadekobot.readthedocs.io/en/latest/).")); + return Task.FromResult(PreconditionResult.FromError("Not available on the public bot. To learn how to selfhost a private bot, click [here](https://nadekobot.readthedocs.io/en/latest/).")); #else return Task.FromResult(PreconditionResult.FromSuccess()); #endif diff --git a/src/NadekoBot/Common/OptionsParser.cs b/src/NadekoBot/Common/OptionsParser.cs index 106eebb09..ae6c85e69 100644 --- a/src/NadekoBot/Common/OptionsParser.cs +++ b/src/NadekoBot/Common/OptionsParser.cs @@ -4,15 +4,18 @@ namespace NadekoBot.Common; public static class OptionsParser { - public static T ParseFrom(string[] args) where T : INadekoCommandOptions, new() + public static T ParseFrom(string[] args) + where T : INadekoCommandOptions, new() => ParseFrom(new T(), args).Item1; - public static (T, bool) ParseFrom(T options, string[] args) where T : INadekoCommandOptions + public static (T, bool) ParseFrom(T options, string[] args) + where T : INadekoCommandOptions { using var p = new Parser(x => - { - x.HelpWriter = null; - }); + { + x.HelpWriter = null; + } + ); var res = p.ParseArguments(args); options = res.MapResult(x => x, x => options); options.NormalizeOptions(); diff --git a/src/NadekoBot/Common/OsuUserBets.cs b/src/NadekoBot/Common/OsuUserBets.cs index 9954848a0..97c376306 100644 --- a/src/NadekoBot/Common/OsuUserBets.cs +++ b/src/NadekoBot/Common/OsuUserBets.cs @@ -4,37 +4,54 @@ namespace NadekoBot.Common; public class OsuUserBests { - [JsonProperty("beatmap_id")] public string BeatmapId { get; set; } + [JsonProperty("beatmap_id")] + public string BeatmapId { get; set; } - [JsonProperty("score_id")] public string ScoreId { get; set; } + [JsonProperty("score_id")] + public string ScoreId { get; set; } - [JsonProperty("score")] public string Score { get; set; } + [JsonProperty("score")] + public string Score { get; set; } - [JsonProperty("maxcombo")] public string Maxcombo { get; set; } + [JsonProperty("maxcombo")] + public string Maxcombo { get; set; } - [JsonProperty("count50")] public double Count50 { get; set; } + [JsonProperty("count50")] + public double Count50 { get; set; } - [JsonProperty("count100")] public double Count100 { get; set; } + [JsonProperty("count100")] + public double Count100 { get; set; } - [JsonProperty("count300")] public double Count300 { get; set; } + [JsonProperty("count300")] + public double Count300 { get; set; } - [JsonProperty("countmiss")] public int Countmiss { get; set; } + [JsonProperty("countmiss")] + public int Countmiss { get; set; } - [JsonProperty("countkatu")] public double Countkatu { get; set; } + [JsonProperty("countkatu")] + public double Countkatu { get; set; } - [JsonProperty("countgeki")] public double Countgeki { get; set; } + [JsonProperty("countgeki")] + public double Countgeki { get; set; } - [JsonProperty("perfect")] public string Perfect { get; set; } + [JsonProperty("perfect")] + public string Perfect { get; set; } - [JsonProperty("enabled_mods")] public int EnabledMods { get; set; } + [JsonProperty("enabled_mods")] + public int EnabledMods { get; set; } - [JsonProperty("user_id")] public string UserId { get; set; } + [JsonProperty("user_id")] + public string UserId { get; set; } - [JsonProperty("date")] public string Date { get; set; } + [JsonProperty("date")] + public string Date { get; set; } - [JsonProperty("rank")] public string Rank { get; set; } + [JsonProperty("rank")] + public string Rank { get; set; } - [JsonProperty("pp")] public double Pp { get; set; } + [JsonProperty("pp")] + public double Pp { get; set; } - [JsonProperty("replay_available")] public string ReplayAvailable { get; set; } + [JsonProperty("replay_available")] + public string ReplayAvailable { get; set; } } \ No newline at end of file diff --git a/src/NadekoBot/Common/PlatformHelper.cs b/src/NadekoBot/Common/PlatformHelper.cs index a8f9d9549..fc3fcbafd 100644 --- a/src/NadekoBot/Common/PlatformHelper.cs +++ b/src/NadekoBot/Common/PlatformHelper.cs @@ -2,21 +2,24 @@ public static class PlatformHelper { - private const int ProcessorCountRefreshIntervalMs = 30000; + private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; - private static volatile int _processorCount; - private static volatile int _lastProcessorCountRefreshTicks; + private static volatile int processorCount; + private static volatile int lastProcessorCountRefreshTicks; - public static int ProcessorCount { - get { + public static int ProcessorCount + { + get + { var now = Environment.TickCount; - if (_processorCount == 0 || now - _lastProcessorCountRefreshTicks >= ProcessorCountRefreshIntervalMs) + if (processorCount == 0 || + now - lastProcessorCountRefreshTicks >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS) { - _processorCount = Environment.ProcessorCount; - _lastProcessorCountRefreshTicks = now; + processorCount = Environment.ProcessorCount; + lastProcessorCountRefreshTicks = now; } - return _processorCount; + return processorCount; } } } \ No newline at end of file diff --git a/src/NadekoBot/Common/Pokemon/SearchPokemon.cs b/src/NadekoBot/Common/Pokemon/SearchPokemon.cs index ba57cabc0..d99845531 100644 --- a/src/NadekoBot/Common/Pokemon/SearchPokemon.cs +++ b/src/NadekoBot/Common/Pokemon/SearchPokemon.cs @@ -12,19 +12,21 @@ public class SearchPokemon public class BaseStatsClass { - public int HP { get; set; } - public int ATK { get; set; } - public int DEF { get; set; } - public int SPA { get; set; } - public int SPD { get; set; } - public int SPE { get; set; } + public int Hp { get; set; } + public int Atk { get; set; } + public int Def { get; set; } + public int Spa { get; set; } + public int Spd { get; set; } + public int Spe { get; set; } - public override string ToString() => $@"💚**HP:** {HP,-4} ⚔**ATK:** {ATK,-4} 🛡**DEF:** {DEF,-4} -✨**SPA:** {SPA,-4} 🎇**SPD:** {SPD,-4} 💨**SPE:** {SPE,-4}"; + public override string ToString() + => $@"💚**HP:** {Hp,-4} ⚔**ATK:** {Atk,-4} 🛡**DEF:** {Def,-4} +✨**SPA:** {Spa,-4} 🎇**SPD:** {Spd,-4} 💨**SPE:** {Spe,-4}"; } [JsonProperty("num")] public int Id { get; set; } + public string Species { get; set; } public string[] Types { get; set; } public GenderRatioClass GenderRatio { get; set; } diff --git a/src/NadekoBot/Common/PubSub/EventPubSub.cs b/src/NadekoBot/Common/PubSub/EventPubSub.cs index fee3772c3..eada3d880 100644 --- a/src/NadekoBot/Common/PubSub/EventPubSub.cs +++ b/src/NadekoBot/Common/PubSub/EventPubSub.cs @@ -3,22 +3,20 @@ public class EventPubSub : IPubSub { private readonly Dictionary>>> _actions = new(); - private readonly object locker = new(); - + private readonly object _locker = new(); + public Task Sub(in TypedKey key, Func action) { - Func localAction = obj => action((TData) obj); - lock(locker) + Func localAction = obj => action((TData)obj); + lock (_locker) { - Dictionary>> keyActions; - if (!_actions.TryGetValue(key.Key, out keyActions)) + if (!_actions.TryGetValue(key.Key, out var keyActions)) { keyActions = new(); _actions[key.Key] = keyActions; } - List> sameActions; - if (!keyActions.TryGetValue(action, out sameActions)) + if (!keyActions.TryGetValue(action, out var sameActions)) { sameActions = new(); keyActions[action] = sameActions; @@ -29,19 +27,17 @@ public class EventPubSub : IPubSub return Task.CompletedTask; } } - + public Task Pub(in TypedKey key, TData data) { - lock (locker) + lock (_locker) { - if(_actions.TryGetValue(key.Key, out var actions)) + if (_actions.TryGetValue(key.Key, out var actions)) { // if this class ever gets used, this needs to be properly implemented // 1. ignore all valuetasks which are completed // 2. return task.whenall all other tasks - return Task.WhenAll(actions - .SelectMany(kvp => kvp.Value) - .Select(action => action(data).AsTask())); + return Task.WhenAll(actions.SelectMany(kvp => kvp.Value).Select(action => action(data).AsTask())); } return Task.CompletedTask; @@ -50,12 +46,11 @@ public class EventPubSub : IPubSub public Task Unsub(in TypedKey key, Func action) { - lock (locker) + lock (_locker) { // get subscriptions for this action if (_actions.TryGetValue(key.Key, out var actions)) { - var hashCode = action.GetHashCode(); // get subscriptions which have the same action hash code // note: having this as a list allows for multiple subscriptions of // the same insance's/static method @@ -63,13 +58,13 @@ public class EventPubSub : IPubSub { // remove last subscription sameActions.RemoveAt(sameActions.Count - 1); - + // if the last subscription was the only subscription // we can safely remove this action's dictionary entry if (sameActions.Count == 0) { actions.Remove(action); - + // if our dictionary has no more elements after // removing the entry // it's safe to remove it from the key's subscriptions diff --git a/src/NadekoBot/Common/PubSub/JsonSeria.cs b/src/NadekoBot/Common/PubSub/JsonSeria.cs index 4c3cad887..e60d805c0 100644 --- a/src/NadekoBot/Common/PubSub/JsonSeria.cs +++ b/src/NadekoBot/Common/PubSub/JsonSeria.cs @@ -5,23 +5,19 @@ namespace NadekoBot.Common; public class JsonSeria : ISeria { - private readonly JsonSerializerOptions serializerOptions = new() + private readonly JsonSerializerOptions _serializerOptions = new() { - Converters = - { - new Rgba32Converter(), - new CultureInfoConverter(), - } + Converters = { new Rgba32Converter(), new CultureInfoConverter(), } }; - public byte[] Serialize(T data) - => JsonSerializer.SerializeToUtf8Bytes(data, serializerOptions); + + public byte[] Serialize(T data) + => JsonSerializer.SerializeToUtf8Bytes(data, _serializerOptions); public T Deserialize(byte[] data) { if (data is null) return default; - - return JsonSerializer.Deserialize(data, serializerOptions); + return JsonSerializer.Deserialize(data, _serializerOptions); } } \ No newline at end of file diff --git a/src/NadekoBot/Common/PubSub/RedisPubSub.cs b/src/NadekoBot/Common/PubSub/RedisPubSub.cs index 0e131dcb7..722c5a4cd 100644 --- a/src/NadekoBot/Common/PubSub/RedisPubSub.cs +++ b/src/NadekoBot/Common/PubSub/RedisPubSub.cs @@ -18,13 +18,15 @@ public sealed class RedisPubSub : IPubSub public Task Pub(in TypedKey key, TData data) { var serialized = _serializer.Serialize(data); - return _multi.GetSubscriber().PublishAsync($"{_creds.RedisKey()}:{key.Key}", serialized, CommandFlags.FireAndForget); + return _multi.GetSubscriber() + .PublishAsync($"{_creds.RedisKey()}:{key.Key}", serialized, CommandFlags.FireAndForget); } public Task Sub(in TypedKey key, Func action) { var eventName = key.Key; - return _multi.GetSubscriber().SubscribeAsync($"{_creds.RedisKey()}:{eventName}", async (ch, data) => + + async void OnSubscribeHandler(RedisChannel _, RedisValue data) { try { @@ -33,8 +35,10 @@ public sealed class RedisPubSub : IPubSub } catch (Exception ex) { - Log.Error($"Error handling the event {eventName}: {ex.Message}"); + Log.Error("Error handling the event {EventName}: {ErrorMessage}", eventName, ex.Message); } - }); + } + + return _multi.GetSubscriber().SubscribeAsync($"{_creds.RedisKey()}:{eventName}", OnSubscribeHandler); } } \ No newline at end of file diff --git a/src/NadekoBot/Common/PubSub/TypedKey.cs b/src/NadekoBot/Common/PubSub/TypedKey.cs index 3349bdd1c..d03f81e0f 100644 --- a/src/NadekoBot/Common/PubSub/TypedKey.cs +++ b/src/NadekoBot/Common/PubSub/TypedKey.cs @@ -9,18 +9,22 @@ public readonly struct TypedKey public static implicit operator TypedKey(in string input) => new(input); + public static implicit operator string(in TypedKey input) => input.Key; public static bool operator ==(in TypedKey left, in TypedKey right) => left.Key == right.Key; + public static bool operator !=(in TypedKey left, in TypedKey right) => !(left == right); public override bool Equals(object obj) => obj is TypedKey o && o == this; - public override int GetHashCode() => Key?.GetHashCode() ?? 0; + public override int GetHashCode() + => Key?.GetHashCode() ?? 0; - public override string ToString() => Key; + public override string ToString() + => Key; } \ No newline at end of file diff --git a/src/NadekoBot/Common/PubSub/YamlSeria.cs b/src/NadekoBot/Common/PubSub/YamlSeria.cs index 8f1453e1c..3f3784961 100644 --- a/src/NadekoBot/Common/PubSub/YamlSeria.cs +++ b/src/NadekoBot/Common/PubSub/YamlSeria.cs @@ -10,28 +10,31 @@ public class YamlSeria : IConfigSeria private readonly ISerializer _serializer; private readonly IDeserializer _deserializer; - private static readonly Regex CodePointRegex - = new(@"(\\U(?[a-zA-Z0-9]{8})|\\u(?[a-zA-Z0-9]{4})|\\x(?[a-zA-Z0-9]{2}))", - RegexOptions.Compiled); + private static readonly Regex _codePointRegex = + new(@"(\\U(?[a-zA-Z0-9]{8})|\\u(?[a-zA-Z0-9]{4})|\\x(?[a-zA-Z0-9]{2}))", + RegexOptions.Compiled + ); public YamlSeria() { _serializer = Yaml.Serializer; _deserializer = Yaml.Deserializer; } - + public string Serialize(T obj) { var escapedOutput = _serializer.Serialize(obj); - var output = CodePointRegex.Replace(escapedOutput, me => - { - var str = me.Groups["code"].Value; - var newString = YamlHelper.UnescapeUnicodeCodePoint(str); - return newString; - }); + var output = _codePointRegex.Replace(escapedOutput, + me => + { + var str = me.Groups["code"].Value; + var newString = YamlHelper.UnescapeUnicodeCodePoint(str); + return newString; + } + ); return output; } - public T Deserialize(string data) + public T Deserialize(string data) => _deserializer.Deserialize(data); } \ No newline at end of file diff --git a/src/NadekoBot/Common/Replacements/ReplacementBuilder.cs b/src/NadekoBot/Common/Replacements/ReplacementBuilder.cs index fb82932b8..b31c26daf 100644 --- a/src/NadekoBot/Common/Replacements/ReplacementBuilder.cs +++ b/src/NadekoBot/Common/Replacements/ReplacementBuilder.cs @@ -5,21 +5,29 @@ namespace NadekoBot.Common; public class ReplacementBuilder { - private static readonly Regex rngRegex = new("%rng(?:(?(?:-)?\\d+)-(?(?:-)?\\d+))?%", RegexOptions.Compiled); + private static readonly Regex _rngRegex = new("%rng(?:(?(?:-)?\\d+)-(?(?:-)?\\d+))?%", + RegexOptions.Compiled + ); + private readonly ConcurrentDictionary> _reps = new(); private readonly ConcurrentDictionary> _regex = new(); public ReplacementBuilder() => WithRngRegex(); - public ReplacementBuilder WithDefault(IUser usr, IMessageChannel ch, SocketGuild g, DiscordSocketClient client) - => this.WithUser(usr) - .WithChannel(ch) - .WithServer(client, g) - .WithClient(client); + public ReplacementBuilder WithDefault( + IUser usr, + IMessageChannel ch, + SocketGuild g, + DiscordSocketClient client) + => this.WithUser(usr).WithChannel(ch).WithServer(client, g).WithClient(client); - public ReplacementBuilder WithDefault(ICommandContext ctx) => - WithDefault(ctx.User, ctx.Channel, ctx.Guild as SocketGuild, (DiscordSocketClient)ctx.Client); + public ReplacementBuilder WithDefault(ICommandContext ctx) + => WithDefault(ctx.User, + ctx.Channel, + ctx.Guild as SocketGuild, + (DiscordSocketClient)ctx.Client + ); public ReplacementBuilder WithMention(DiscordSocketClient client) { @@ -30,12 +38,14 @@ public class ReplacementBuilder public ReplacementBuilder WithClient(DiscordSocketClient client) { WithMention(client); - + _reps.TryAdd("%bot.status%", () => client.Status.ToString()); _reps.TryAdd("%bot.latency%", () => client.Latency.ToString()); _reps.TryAdd("%bot.name%", () => client.CurrentUser.Username); _reps.TryAdd("%bot.fullname%", () => client.CurrentUser.ToString()); - _reps.TryAdd("%bot.time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials())); + _reps.TryAdd("%bot.time%", + () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) + ); _reps.TryAdd("%bot.discrim%", () => client.CurrentUser.Discriminator); _reps.TryAdd("%bot.id%", () => client.CurrentUser.Id.ToString()); _reps.TryAdd("%bot.avatar%", () => client.CurrentUser.RealAvatarUrl()?.ToString()); @@ -51,19 +61,20 @@ public class ReplacementBuilder _reps.TryAdd("%server.members%", () => g is { } sg ? sg.MemberCount.ToString() : "?"); _reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString()); _reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString()); - _reps.TryAdd("%server.time%", () => - { - var to = TimeZoneInfo.Local; - if (g != null) + _reps.TryAdd("%server.time%", + () => { - if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz)) - to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local; - } + var to = TimeZoneInfo.Local; + if (g != null) + { + if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz)) + to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local; + } - return TimeZoneInfo.ConvertTime(DateTime.UtcNow, - TimeZoneInfo.Utc, - to).ToString("HH:mm ") + to.StandardName.GetInitials(); - }); + return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ") + + to.StandardName.GetInitials(); + } + ); return this; } @@ -80,7 +91,7 @@ public class ReplacementBuilder public ReplacementBuilder WithUser(IUser user) { - WithManyUsers(new[] {user}); + WithManyUsers(new[] { user }); return this; } @@ -92,10 +103,18 @@ public class ReplacementBuilder _reps.TryAdd("%user.discrim%", () => string.Join(" ", users.Select(user => user.Discriminator))); _reps.TryAdd("%user.avatar%", () => string.Join(" ", users.Select(user => user.RealAvatarUrl()?.ToString()))); _reps.TryAdd("%user.id%", () => string.Join(" ", users.Select(user => user.Id.ToString()))); - _reps.TryAdd("%user.created_time%", () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("HH:mm")))); - _reps.TryAdd("%user.created_date%", () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("dd.MM.yyyy")))); - _reps.TryAdd("%user.joined_time%", () => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("HH:mm") ?? "-"))); - _reps.TryAdd("%user.joined_date%", () => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("dd.MM.yyyy") ?? "-"))); + _reps.TryAdd("%user.created_time%", + () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("HH:mm"))) + ); + _reps.TryAdd("%user.created_date%", + () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("dd.MM.yyyy"))) + ); + _reps.TryAdd("%user.joined_time%", + () => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("HH:mm") ?? "-")) + ); + _reps.TryAdd("%user.joined_date%", + () => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("dd.MM.yyyy") ?? "-")) + ); return this; } @@ -110,21 +129,24 @@ public class ReplacementBuilder public ReplacementBuilder WithRngRegex() { var rng = new NadekoRandom(); - _regex.TryAdd(rngRegex, match => - { - if (!int.TryParse(match.Groups["from"].ToString(), out var from)) - from = 0; - if (!int.TryParse(match.Groups["to"].ToString(), out var to)) - to = 0; + _regex.TryAdd(_rngRegex, + match => + { + if (!int.TryParse(match.Groups["from"].ToString(), out var from)) + from = 0; + if (!int.TryParse(match.Groups["to"].ToString(), out var to)) + to = 0; - if (from == 0 && to == 0) - return rng.Next(0, 11).ToString(); + if (from == 0 && + to == 0) + return rng.Next(0, 11).ToString(); - if (from >= to) - return string.Empty; + if (from >= to) + return string.Empty; - return rng.Next(from, to + 1).ToString(); - }); + return rng.Next(from, to + 1).ToString(); + } + ); return this; } diff --git a/src/NadekoBot/Common/Replacements/Replacer.cs b/src/NadekoBot/Common/Replacements/Replacer.cs index 8e8dc31aa..210f889cd 100644 --- a/src/NadekoBot/Common/Replacements/Replacer.cs +++ b/src/NadekoBot/Common/Replacements/Replacer.cs @@ -18,10 +18,10 @@ public class Replacer if (string.IsNullOrWhiteSpace(input)) return input; - foreach (var (Key, Text) in _replacements) + foreach (var (key, text) in _replacements) { - if (input.Contains(Key)) - input = input.Replace(Key, Text(), StringComparison.InvariantCulture); + if (input.Contains(key)) + input = input.Replace(key, text(), StringComparison.InvariantCulture); } foreach (var item in _regex) @@ -58,8 +58,7 @@ public class Replacer { newEmbedData.Author = new() { - Name = Replace(embedData.Author.Name), - IconUrl = Replace(embedData.Author.IconUrl) + Name = Replace(embedData.Author.Name), IconUrl = Replace(embedData.Author.IconUrl) }; } @@ -70,9 +69,7 @@ public class Replacer { var newF = new SmartTextEmbedField { - Name = Replace(f.Name), - Value = Replace(f.Value), - Inline = f.Inline + Name = Replace(f.Name), Value = Replace(f.Value), Inline = f.Inline }; fields.Add(newF); } @@ -84,8 +81,7 @@ public class Replacer { newEmbedData.Footer = new() { - Text = Replace(embedData.Footer.Text), - IconUrl = Replace(embedData.Footer.IconUrl) + Text = Replace(embedData.Footer.Text), IconUrl = Replace(embedData.Footer.IconUrl) }; } diff --git a/src/NadekoBot/Common/ShmartNumber.cs b/src/NadekoBot/Common/ShmartNumber.cs index 297bfadbd..2f8d1729a 100644 --- a/src/NadekoBot/Common/ShmartNumber.cs +++ b/src/NadekoBot/Common/ShmartNumber.cs @@ -24,9 +24,7 @@ public struct ShmartNumber : IEquatable => Value.ToString(); public override bool Equals(object obj) - => obj is ShmartNumber sn - ? Equals(sn) - : false; + => obj is ShmartNumber sn && Equals(sn); public bool Equals(ShmartNumber other) => other.Value == Value; diff --git a/src/NadekoBot/Common/SmartText/SmartEmbedText.cs b/src/NadekoBot/Common/SmartText/SmartEmbedText.cs index 6a42a3e1e..3ef18b984 100644 --- a/src/NadekoBot/Common/SmartText/SmartEmbedText.cs +++ b/src/NadekoBot/Common/SmartText/SmartEmbedText.cs @@ -15,14 +15,13 @@ public sealed record SmartEmbedText : SmartText public uint Color { get; set; } = 7458112; - public bool IsValid => - !string.IsNullOrWhiteSpace(Title) || - !string.IsNullOrWhiteSpace(Description) || - !string.IsNullOrWhiteSpace(Url) || - !string.IsNullOrWhiteSpace(Thumbnail) || - !string.IsNullOrWhiteSpace(Image) || - (Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) || - Fields is { Length: > 0 }; + public bool IsValid + => !string.IsNullOrWhiteSpace(Title) || !string.IsNullOrWhiteSpace(Description) || + !string.IsNullOrWhiteSpace(Url) || !string.IsNullOrWhiteSpace(Thumbnail) || + !string.IsNullOrWhiteSpace(Image) || + (Footer != null && + (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) || + Fields is { Length: > 0 }; public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null) { @@ -34,42 +33,23 @@ public sealed record SmartEmbedText : SmartText 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 + 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 => new SmartTextEmbedField() - { - Inline = field.Inline, - Name = field.Name, - Value = field.Value, - }) + set.Fields = eb.Fields.Select(field + => new SmartTextEmbedField() { Inline = field.Inline, Name = field.Name, Value = field.Value, } + ) .ToArray(); set.Color = eb.Color?.RawValue ?? 0; return set; } - + public EmbedBuilder GetEmbed() { - var embed = new EmbedBuilder() - .WithColor(Color); + var embed = new EmbedBuilder().WithColor(Color); if (!string.IsNullOrWhiteSpace(Title)) embed.WithTitle(Title); @@ -77,26 +57,31 @@ public sealed record SmartEmbedText : SmartText if (!string.IsNullOrWhiteSpace(Description)) embed.WithDescription(Description); - if (Url != null && Uri.IsWellFormedUriString(Url, UriKind.Absolute)) + if (Url != null && + Uri.IsWellFormedUriString(Url, UriKind.Absolute)) embed.WithUrl(Url); if (Footer != null) { embed.WithFooter(efb => - { - efb.WithText(Footer.Text); - if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute)) - efb.WithIconUrl(Footer.IconUrl); - }); + { + efb.WithText(Footer.Text); + if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute)) + efb.WithIconUrl(Footer.IconUrl); + } + ); } - if (Thumbnail != null && Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute)) + if (Thumbnail != null && + Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute)) embed.WithThumbnailUrl(Thumbnail); - if (Image != null && Uri.IsWellFormedUriString(Image, UriKind.Absolute)) + if (Image != null && + Uri.IsWellFormedUriString(Image, UriKind.Absolute)) embed.WithImageUrl(Image); - if (Author != null && !string.IsNullOrWhiteSpace(Author.Name)) + if (Author != null && + !string.IsNullOrWhiteSpace(Author.Name)) { if (!Uri.IsWellFormedUriString(Author.IconUrl, UriKind.Absolute)) Author.IconUrl = null; @@ -110,7 +95,8 @@ public sealed record SmartEmbedText : SmartText { foreach (var f in Fields) { - if (!string.IsNullOrWhiteSpace(f.Name) && !string.IsNullOrWhiteSpace(f.Value)) + if (!string.IsNullOrWhiteSpace(f.Name) && + !string.IsNullOrWhiteSpace(f.Value)) embed.AddField(f.Name, f.Value, f.Inline); } } diff --git a/src/NadekoBot/Common/SmartText/SmartText.cs b/src/NadekoBot/Common/SmartText/SmartText.cs index e6957d48f..4e7b725cb 100644 --- a/src/NadekoBot/Common/SmartText/SmartText.cs +++ b/src/NadekoBot/Common/SmartText/SmartText.cs @@ -4,8 +4,11 @@ namespace NadekoBot; public abstract record SmartText { - public bool IsEmbed => this is SmartEmbedText; - public bool IsPlainText => this is SmartPlainText; + public bool IsEmbed + => this is SmartEmbedText; + + public bool IsPlainText + => this is SmartPlainText; public static SmartText operator +(SmartText text, string input) => text switch @@ -14,7 +17,7 @@ public abstract record SmartText SmartPlainText spt => new SmartPlainText(spt.Text + input), _ => throw new ArgumentOutOfRangeException(nameof(text)) }; - + public static SmartText operator +(string input, SmartText text) => text switch { @@ -22,10 +25,11 @@ public abstract record SmartText SmartPlainText spt => new SmartPlainText(input + spt.Text), _ => throw new ArgumentOutOfRangeException(nameof(text)) }; - + public static SmartText CreateFrom(string input) { - if (string.IsNullOrWhiteSpace(input) || !input.TrimStart().StartsWith("{")) + if (string.IsNullOrWhiteSpace(input) || + !input.TrimStart().StartsWith("{")) { return new SmartPlainText(input); } @@ -34,6 +38,9 @@ public abstract record SmartText { var smartEmbedText = JsonConvert.DeserializeObject(input); + if (smartEmbedText is null) + throw new(); + smartEmbedText.NormalizeFields(); if (!smartEmbedText.IsValid) diff --git a/src/NadekoBot/Common/SmartText/SmartTextEmbedAuthor.cs b/src/NadekoBot/Common/SmartText/SmartTextEmbedAuthor.cs index ee0bbeb6b..d208f1894 100644 --- a/src/NadekoBot/Common/SmartText/SmartTextEmbedAuthor.cs +++ b/src/NadekoBot/Common/SmartText/SmartTextEmbedAuthor.cs @@ -5,8 +5,7 @@ namespace NadekoBot; public class SmartTextEmbedAuthor { public string Name { get; set; } - public string IconUrl { get; set; } [JsonProperty("icon_url")] - private string Icon_Url { set => IconUrl = value; } + public string IconUrl { get; set; } public string Url { get; set; } } \ No newline at end of file diff --git a/src/NadekoBot/Common/SmartText/SmartTextEmbedFooter.cs b/src/NadekoBot/Common/SmartText/SmartTextEmbedFooter.cs index 1fe0cb445..3176f3d23 100644 --- a/src/NadekoBot/Common/SmartText/SmartTextEmbedFooter.cs +++ b/src/NadekoBot/Common/SmartText/SmartTextEmbedFooter.cs @@ -2,10 +2,11 @@ namespace NadekoBot; +// todo test smarttextembedfooter and smarttextembedauthor + public class SmartTextEmbedFooter { public string Text { get; set; } - public string IconUrl { get; set; } [JsonProperty("icon_url")] - private string Icon_Url { set => IconUrl = value; } + public string IconUrl { get; set; } } \ No newline at end of file diff --git a/src/NadekoBot/Common/SocketMessageEventWrapper.cs b/src/NadekoBot/Common/SocketMessageEventWrapper.cs index 77f8789ef..aafd4aa42 100644 --- a/src/NadekoBot/Common/SocketMessageEventWrapper.cs +++ b/src/NadekoBot/Common/SocketMessageEventWrapper.cs @@ -17,36 +17,37 @@ public sealed class ReactionEventWrapper : IDisposable _client.ReactionsCleared += Discord_ReactionsCleared; } - private Task Discord_ReactionsCleared( - Cacheable msg, - Cacheable channel) + private Task Discord_ReactionsCleared(Cacheable msg, Cacheable channel) { Task.Run(() => - { - try { - if (msg.Id == Message.Id) - OnReactionsCleared?.Invoke(); + try + { + if (msg.Id == Message.Id) + OnReactionsCleared?.Invoke(); + } + catch { } } - catch { } - }); + ); return Task.CompletedTask; } private Task Discord_ReactionRemoved( Cacheable msg, - Cacheable cacheable, SocketReaction reaction) + Cacheable cacheable, + SocketReaction reaction) { Task.Run(() => - { - try { - if (msg.Id == Message.Id) - OnReactionRemoved?.Invoke(reaction); + try + { + if (msg.Id == Message.Id) + OnReactionRemoved?.Invoke(reaction); + } + catch { } } - catch { } - }); + ); return Task.CompletedTask; } @@ -57,16 +58,17 @@ public sealed class ReactionEventWrapper : IDisposable SocketReaction reaction) { Task.Run(() => - { - try { - if (msg.Id == Message.Id) - OnReactionAdded?.Invoke(reaction); + try + { + if (msg.Id == Message.Id) + OnReactionAdded?.Invoke(reaction); + } + catch + { + } } - catch - { - } - }); + ); return Task.CompletedTask; } diff --git a/src/NadekoBot/Common/TypeReaders/BotCommandTypeReader.cs b/src/NadekoBot/Common/TypeReaders/BotCommandTypeReader.cs index 8bb7eddec..ca4768ddf 100644 --- a/src/NadekoBot/Common/TypeReaders/BotCommandTypeReader.cs +++ b/src/NadekoBot/Common/TypeReaders/BotCommandTypeReader.cs @@ -36,10 +36,7 @@ public sealed class CommandOrCrTypeReader : NadekoTypeReader private readonly CustomReactionsService _crs; private readonly CommandHandler _commandHandler; - public CommandOrCrTypeReader( - CommandService cmds, - CustomReactionsService crs, - CommandHandler commandHandler) + public CommandOrCrTypeReader(CommandService cmds, CustomReactionsService crs, CommandHandler commandHandler) { _cmds = cmds; _crs = crs; @@ -58,8 +55,12 @@ public sealed class CommandOrCrTypeReader : NadekoTypeReader var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(context, input).ConfigureAwait(false); if (cmd.IsSuccess) { - return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name, CommandOrCrInfo.Type.Normal)); + return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name, + CommandOrCrInfo.Type.Normal + ) + ); } + return TypeReaderResult.FromError(CommandError.ParseFailed, "No such command or cr found."); } } @@ -74,7 +75,9 @@ public class CommandOrCrInfo public string Name { get; set; } public Type CmdType { get; set; } - public bool IsCustom => CmdType == Type.Custom; + + public bool IsCustom + => CmdType == Type.Custom; public CommandOrCrInfo(string input, Type type) { diff --git a/src/NadekoBot/Common/TypeReaders/GuildDateTimeTypeReader.cs b/src/NadekoBot/Common/TypeReaders/GuildDateTimeTypeReader.cs index 62ece22b9..d6799a3a9 100644 --- a/src/NadekoBot/Common/TypeReaders/GuildDateTimeTypeReader.cs +++ b/src/NadekoBot/Common/TypeReaders/GuildDateTimeTypeReader.cs @@ -12,8 +12,11 @@ public sealed class GuildDateTimeTypeReader : NadekoTypeReader public override Task ReadAsync(ICommandContext context, string input) { var gdt = Parse(context.Guild.Id, input); - if(gdt is null) - return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input string is in an incorrect format.")); + if (gdt is null) + return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, + "Input string is in an incorrect format." + ) + ); return Task.FromResult(TypeReaderResult.FromSuccess(gdt)); } diff --git a/src/NadekoBot/Common/TypeReaders/ModuleTypeReader.cs b/src/NadekoBot/Common/TypeReaders/ModuleTypeReader.cs index 44882b2cd..34204ec62 100644 --- a/src/NadekoBot/Common/TypeReaders/ModuleTypeReader.cs +++ b/src/NadekoBot/Common/TypeReaders/ModuleTypeReader.cs @@ -10,7 +10,9 @@ public sealed class ModuleTypeReader : NadekoTypeReader public override Task ReadAsync(ICommandContext context, string input) { input = input.ToUpperInvariant(); - var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()).FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)?.Key; + var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()) + .FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input) + ?.Key; if (module is null) return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found.")); @@ -28,14 +30,14 @@ public sealed class ModuleOrCrTypeReader : NadekoTypeReader public override Task ReadAsync(ICommandContext context, string input) { input = input.ToUpperInvariant(); - var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()).FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)?.Key; - if (module is null && input != "ACTUALCUSTOMREACTIONS") + var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()) + .FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input) + ?.Key; + if (module is null && + input != "ACTUALCUSTOMREACTIONS") return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found.")); - return Task.FromResult(TypeReaderResult.FromSuccess(new ModuleOrCrInfo - { - Name = input, - })); + return Task.FromResult(TypeReaderResult.FromSuccess(new ModuleOrCrInfo { Name = input, })); } } diff --git a/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs b/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs index 38889ad13..8a1878b3b 100644 --- a/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs +++ b/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs @@ -1,5 +1,6 @@ namespace NadekoBot.Common.TypeReaders; +[MeansImplicitUse(ImplicitUseTargetFlags.Default | ImplicitUseTargetFlags.WithInheritors )] public abstract class NadekoTypeReader : TypeReader { public abstract Task ReadAsync(ICommandContext ctx, string input); diff --git a/src/NadekoBot/Common/TypeReaders/ShmartNumberTypeReader.cs b/src/NadekoBot/Common/TypeReaders/ShmartNumberTypeReader.cs index d9f59737b..eec867d34 100644 --- a/src/NadekoBot/Common/TypeReaders/ShmartNumberTypeReader.cs +++ b/src/NadekoBot/Common/TypeReaders/ShmartNumberTypeReader.cs @@ -63,12 +63,10 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader case "MAX": args.Result = Max(ctx); break; - default: - break; } } - private static readonly Regex percentRegex = new(@"^((?100|\d{1,2})%)$", RegexOptions.Compiled); + private static readonly Regex _percentRegex = new(@"^((?100|\d{1,2})%)$", RegexOptions.Compiled); private long Cur(ICommandContext ctx) { @@ -80,15 +78,13 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader { var settings = _gambling.Data; var max = settings.MaxBet; - return max == 0 - ? Cur(ctx) - : max; + return max == 0 ? Cur(ctx) : max; } private bool TryHandlePercentage(ICommandContext ctx, string input, out long num) { num = 0; - var m = percentRegex.Match(input); + var m = _percentRegex.Match(input); if (m.Captures.Count != 0) { if (!long.TryParse(m.Groups["num"].ToString(), out var percent)) @@ -97,6 +93,7 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader num = (long)(Cur(ctx) * (percent / 100.0f)); return true; } + return false; } } \ No newline at end of file diff --git a/src/NadekoBot/Common/Yml/CommentGatheringTypeInspector.cs b/src/NadekoBot/Common/Yml/CommentGatheringTypeInspector.cs index 22795112d..abc5e9115 100644 --- a/src/NadekoBot/Common/Yml/CommentGatheringTypeInspector.cs +++ b/src/NadekoBot/Common/Yml/CommentGatheringTypeInspector.cs @@ -12,9 +12,7 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton => this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor"); public override IEnumerable GetProperties(Type type, object container) - => innerTypeDescriptor - .GetProperties(type, container) - .Select(d => new CommentsPropertyDescriptor(d)); + => innerTypeDescriptor.GetProperties(type, container).Select(d => new CommentsPropertyDescriptor(d)); private sealed class CommentsPropertyDescriptor : IPropertyDescriptor { @@ -31,14 +29,16 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton public Type Type => baseDescriptor.Type; - public Type TypeOverride { + public Type TypeOverride + { get => baseDescriptor.TypeOverride; set => baseDescriptor.TypeOverride = value; } public int Order { get; set; } - public ScalarStyle ScalarStyle { + public ScalarStyle ScalarStyle + { get => baseDescriptor.ScalarStyle; set => baseDescriptor.ScalarStyle = value; } @@ -49,7 +49,8 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton public void Write(object target, object value) => baseDescriptor.Write(target, value); - public T GetCustomAttribute() where T : Attribute + public T GetCustomAttribute() + where T : Attribute => baseDescriptor.GetCustomAttribute(); public IObjectDescriptor Read(object target) diff --git a/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs b/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs index ce6538c05..429ee1910 100644 --- a/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs +++ b/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs @@ -14,8 +14,8 @@ public class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) { - var commentsDescriptor = value as CommentsObjectDescriptor; - if (commentsDescriptor != null && !string.IsNullOrWhiteSpace(commentsDescriptor.Comment)) + if (value is CommentsObjectDescriptor commentsDescriptor && + !string.IsNullOrWhiteSpace(commentsDescriptor.Comment)) { context.Emit(new Comment(commentsDescriptor.Comment.Replace("\n", "\n# "), false)); } diff --git a/src/NadekoBot/Common/Yml/MultilineScalarFlowStyleEmitter.cs b/src/NadekoBot/Common/Yml/MultilineScalarFlowStyleEmitter.cs index 37726a82c..9a1c477b5 100644 --- a/src/NadekoBot/Common/Yml/MultilineScalarFlowStyleEmitter.cs +++ b/src/NadekoBot/Common/Yml/MultilineScalarFlowStyleEmitter.cs @@ -7,11 +7,12 @@ namespace NadekoBot.Common.Yml; public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter { public MultilineScalarFlowStyleEmitter(IEventEmitter nextEmitter) - : base(nextEmitter) { } + : base(nextEmitter) + { + } public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { - if (typeof(string).IsAssignableFrom(eventInfo.Source.Type)) { var value = eventInfo.Source.Value as string; @@ -19,10 +20,7 @@ public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter { var isMultiLine = value.IndexOfAny(new char[] { '\r', '\n', '\x85', '\x2028', '\x2029' }) >= 0; if (isMultiLine) - eventInfo = new(eventInfo.Source) - { - Style = ScalarStyle.Literal, - }; + eventInfo = new(eventInfo.Source) { Style = ScalarStyle.Literal, }; } } diff --git a/src/NadekoBot/Common/Yml/Rgba32Converter.cs b/src/NadekoBot/Common/Yml/Rgba32Converter.cs index 173d4acc0..827ad0753 100644 --- a/src/NadekoBot/Common/Yml/Rgba32Converter.cs +++ b/src/NadekoBot/Common/Yml/Rgba32Converter.cs @@ -21,11 +21,11 @@ public class Rgba32Converter : IYamlTypeConverter public void WriteYaml(IEmitter emitter, object value, Type type) { var color = (Rgba32)value; - var val = (uint) ((color.B << 0) | (color.G << 8) | (color.R << 16)); + var val = (uint)((color.B << 0) | (color.G << 8) | (color.R << 16)); emitter.Emit(new Scalar(val.ToString("X6").ToLower())); } } - + public class CultureInfoConverter : IYamlTypeConverter { public bool Accepts(Type type) diff --git a/src/NadekoBot/Common/Yml/Yaml.cs b/src/NadekoBot/Common/Yml/Yaml.cs index ce16e3150..1b50f3b39 100644 --- a/src/NadekoBot/Common/Yml/Yaml.cs +++ b/src/NadekoBot/Common/Yml/Yaml.cs @@ -4,22 +4,23 @@ namespace NadekoBot.Common.Yml; public class Yaml { - public static ISerializer Serializer => new SerializerBuilder() - .WithTypeInspector(inner => new CommentGatheringTypeInspector(inner)) - .WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor)) - .WithEventEmitter(args => new MultilineScalarFlowStyleEmitter(args)) - .WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance) - .WithIndentedSequences() - .WithTypeConverter(new Rgba32Converter()) - .WithTypeConverter(new CultureInfoConverter()) - .WithTypeConverter(new UriConverter()) - .Build(); + public static ISerializer Serializer + => new SerializerBuilder().WithTypeInspector(inner => new CommentGatheringTypeInspector(inner)) + .WithEmissionPhaseObjectGraphVisitor(args => new CommentsObjectGraphVisitor(args.InnerVisitor)) + .WithEventEmitter(args => new MultilineScalarFlowStyleEmitter(args)) + .WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance) + .WithIndentedSequences() + .WithTypeConverter(new Rgba32Converter()) + .WithTypeConverter(new CultureInfoConverter()) + .WithTypeConverter(new UriConverter()) + .Build(); - public static IDeserializer Deserializer => new DeserializerBuilder() - .WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance) - .WithTypeConverter(new Rgba32Converter()) - .WithTypeConverter(new CultureInfoConverter()) - .WithTypeConverter(new UriConverter()) - .IgnoreUnmatchedProperties() - .Build(); + public static IDeserializer Deserializer + => new DeserializerBuilder() + .WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance) + .WithTypeConverter(new Rgba32Converter()) + .WithTypeConverter(new CultureInfoConverter()) + .WithTypeConverter(new UriConverter()) + .IgnoreUnmatchedProperties() + .Build(); } \ No newline at end of file diff --git a/src/NadekoBot/Common/Yml/YamlHelper.cs b/src/NadekoBot/Common/Yml/YamlHelper.cs index 684f82f98..2c3f0f506 100644 --- a/src/NadekoBot/Common/Yml/YamlHelper.cs +++ b/src/NadekoBot/Common/Yml/YamlHelper.cs @@ -15,27 +15,28 @@ public class YamlHelper // Scan the character value. - foreach(var c in point) + foreach (var c in point) { if (!IsHex(c)) { return point; } + character = (character << 4) + AsHex(c); } // Check the value and write the character. - if (character is >= 0xD800 and <= 0xDFFF or > 0x10FFFF) + if (character is (>= 0xD800 and <= 0xDFFF) or > 0x10FFFF) { return point; } return char.ConvertFromUtf32(character); } - + public static bool IsHex(char c) - => c is >= '0' and <= '9' or >= 'A' and <= 'F' or >= 'a' and <= 'f'; + => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f'); public static int AsHex(char c) { @@ -43,10 +44,12 @@ public class YamlHelper { return c - '0'; } + if (c <= 'F') { return c - 'A' + 10; } + return c - 'a' + 10; } } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/ClubExtensions.cs b/src/NadekoBot/Db/Extensions/ClubExtensions.cs index a5f5e6325..39a47d85f 100644 --- a/src/NadekoBot/Db/Extensions/ClubExtensions.cs +++ b/src/NadekoBot/Db/Extensions/ClubExtensions.cs @@ -13,12 +13,13 @@ public static class ClubExtensions .ThenInclude(x => x.User) .Include(x => x.Users) .AsQueryable(); + public static ClubInfo GetByOwner(this DbSet clubs, ulong userId) => Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId); - + public static ClubInfo GetByOwnerOrAdmin(this DbSet clubs, ulong userId) - => Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId - || c.Users.Any(u => u.UserId == userId && u.IsClubAdmin)); + => Include(clubs) + .FirstOrDefault(c => c.Owner.UserId == userId || c.Users.Any(u => u.UserId == userId && u.IsClubAdmin)); public static ClubInfo GetByMember(this DbSet clubs, ulong userId) => Include(clubs).FirstOrDefault(c => c.Users.Any(u => u.UserId == userId)); @@ -27,17 +28,9 @@ public static class ClubExtensions => Include(clubs).FirstOrDefault(c => c.Name.ToUpper() == name.ToUpper() && c.Discrim == discrim); public static int GetNextDiscrim(this DbSet clubs, string name) - => Include(clubs) - .Where(x => x.Name.ToUpper() == name.ToUpper()) - .Select(x => x.Discrim) - .DefaultIfEmpty() - .Max() + 1; + => Include(clubs).Where(x => x.Name.ToUpper() == name.ToUpper()).Select(x => x.Discrim).DefaultIfEmpty().Max() + + 1; public static List GetClubLeaderboardPage(this DbSet clubs, int page) - => clubs - .AsNoTracking() - .OrderByDescending(x => x.Xp) - .Skip(page * 9) - .Take(9) - .ToList(); + => clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList(); } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/CustomReactionsExtensions.cs b/src/NadekoBot/Db/Extensions/CustomReactionsExtensions.cs index a56459e26..cdf4eec59 100644 --- a/src/NadekoBot/Db/Extensions/CustomReactionsExtensions.cs +++ b/src/NadekoBot/Db/Extensions/CustomReactionsExtensions.cs @@ -10,11 +10,7 @@ public static class CustomReactionsExtensions => crs.Delete(x => x.GuildId == guildId); public static IEnumerable ForId(this DbSet crs, ulong id) - => crs - .AsNoTracking() - .AsQueryable() - .Where(x => x.GuildId == id) - .ToArray(); + => crs.AsNoTracking().AsQueryable().Where(x => x.GuildId == id).ToList(); public static CustomReaction GetByGuildIdAndInput(this DbSet crs, ulong? guildId, string input) => crs.FirstOrDefault(x => x.GuildId == guildId && x.Trigger.ToUpper() == input); diff --git a/src/NadekoBot/Db/Extensions/DbExtensions.cs b/src/NadekoBot/Db/Extensions/DbExtensions.cs index 9ed6a45cc..22dfb68e9 100644 --- a/src/NadekoBot/Db/Extensions/DbExtensions.cs +++ b/src/NadekoBot/Db/Extensions/DbExtensions.cs @@ -5,6 +5,7 @@ namespace NadekoBot.Db; public static class DbExtensions { - public static T GetById(this DbSet set, int id) where T: DbEntity + public static T GetById(this DbSet set, int id) + where T : DbEntity => set.FirstOrDefault(x => x.Id == id); } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs b/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs index 99d2673e8..2cafc316d 100644 --- a/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs +++ b/src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs @@ -8,10 +8,15 @@ namespace NadekoBot.Db; public static class DiscordUserExtensions { - public static void EnsureUserCreated(this NadekoContext ctx, ulong userId, string username, string discrim, string avatarId) - => ctx.DiscordUser - .ToLinqToDBTable() - .InsertOrUpdate(() => new() + public static void EnsureUserCreated( + this NadekoContext ctx, + ulong userId, + string username, + string discrim, + string avatarId) + => ctx.DiscordUser.ToLinqToDBTable() + .InsertOrUpdate( + () => new() { UserId = userId, Username = username, @@ -20,36 +25,44 @@ public static class DiscordUserExtensions TotalXp = 0, CurrencyAmount = 0 }, - old => new() - { - Username = username, - Discriminator = discrim, - AvatarId = avatarId, - }, () => new() - { - UserId = userId - }); + old => new() { Username = username, Discriminator = discrim, AvatarId = avatarId, }, + () => new() { UserId = userId } + ); //temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown - public static DiscordUser GetOrCreateUser(this NadekoContext ctx, ulong userId, string username, string discrim, string avatarId) + public static DiscordUser GetOrCreateUser( + this NadekoContext ctx, + ulong userId, + string username, + string discrim, + string avatarId) { - ctx.EnsureUserCreated(userId, username, discrim, avatarId); - return ctx.DiscordUser - .Include(x => x.Club) + ctx.EnsureUserCreated(userId, + username, + discrim, + avatarId + ); + return ctx.DiscordUser.Include(x => x.Club) .First(u => u.UserId == userId); } public static DiscordUser GetOrCreateUser(this NadekoContext ctx, IUser original) - => ctx.GetOrCreateUser(original.Id, original.Username, original.Discriminator, original.AvatarId); + => ctx.GetOrCreateUser(original.Id, + original.Username, + original.Discriminator, + original.AvatarId + ); public static int GetUserGlobalRank(this DbSet users, ulong id) => users.AsQueryable() - .Where(x => x.TotalXp > users - .AsQueryable() - .Where(y => y.UserId == id) - .Select(y => y.TotalXp) - .FirstOrDefault()) - .Count() + 1; + .Where(x => x.TotalXp > + users.AsQueryable() + .Where(y => y.UserId == id) + .Select(y => y.TotalXp) + .FirstOrDefault() + ) + .Count() + + 1; public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet users, int page) => users.AsQueryable() @@ -59,7 +72,11 @@ public static class DiscordUserExtensions .AsEnumerable() .ToArray(); - public static List GetTopRichest(this DbSet users, ulong botId, int count, int page = 0) + public static List GetTopRichest( + this DbSet users, + ulong botId, + int count, + int page = 0) => users.AsQueryable() .Where(c => c.CurrencyAmount > 0 && botId != c.UserId) .OrderByDescending(c => c.CurrencyAmount) @@ -67,40 +84,44 @@ public static class DiscordUserExtensions .Take(count) .ToList(); - public static List GetTopRichest(this DbSet users, ulong botId, int count) - => users.AsQueryable() - .Where(c => c.CurrencyAmount > 0 && botId != c.UserId) - .OrderByDescending(c => c.CurrencyAmount) - .Take(count) - .ToList(); - - public static long GetUserCurrency(this DbSet users, ulong userId) => - users.AsNoTracking() - .FirstOrDefault(x => x.UserId == userId) - ?.CurrencyAmount ?? 0; + public static long GetUserCurrency(this DbSet users, ulong userId) + => users.AsNoTracking() + .FirstOrDefault(x => x.UserId == userId) + ?.CurrencyAmount ?? + 0; public static void RemoveFromMany(this DbSet users, IEnumerable ids) { - var items = users.AsQueryable().Where(x => ids.Contains(x.UserId)); + var items = users.AsQueryable() + .Where(x => ids.Contains(x.UserId)); foreach (var item in items) { item.CurrencyAmount = 0; } } - public static bool TryUpdateCurrencyState(this NadekoContext ctx, ulong userId, string name, string discrim, string avatarId, long amount, bool allowNegative = false) + public static bool TryUpdateCurrencyState( + this NadekoContext ctx, + ulong userId, + string name, + string discrim, + string avatarId, + long amount, + bool allowNegative = false) { if (amount == 0) return true; // if remove - try to remove if he has more or equal than the amount // and return number of rows > 0 (was there a change) - if (amount < 0 && !allowNegative) + if (amount < 0 && + !allowNegative) { var rows = ctx.Database.ExecuteSqlInterpolated($@" UPDATE DiscordUser SET CurrencyAmount=CurrencyAmount+{amount} -WHERE UserId={userId} AND CurrencyAmount>={-amount};"); +WHERE UserId={userId} AND CurrencyAmount>={-amount};" + ); return rows > 0; } @@ -110,7 +131,8 @@ WHERE UserId={userId} AND CurrencyAmount>={-amount};"); var rows = ctx.Database.ExecuteSqlInterpolated($@" UPDATE DiscordUser SET CurrencyAmount=CurrencyAmount+{amount} -WHERE UserId={userId};"); +WHERE UserId={userId};" + ); return rows > 0; } @@ -118,22 +140,22 @@ WHERE UserId={userId};"); // if it exists, sum current amount with the new one, if it doesn't // he just has the new amount var updatedUserData = !string.IsNullOrWhiteSpace(name); - name = name ?? "Unknown"; - discrim = discrim ?? "????"; - avatarId = avatarId ?? ""; + name ??= "Unknown"; + discrim ??= "????"; + avatarId ??= ""; // just update the amount, there is no new user data if (!updatedUserData) { - var rows = ctx.Database.ExecuteSqlInterpolated($@" + ctx.Database.ExecuteSqlInterpolated($@" UPDATE OR IGNORE DiscordUser SET CurrencyAmount=CurrencyAmount+{amount} WHERE UserId={userId}; INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp) VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0); -"); - +" + ); } else { @@ -147,14 +169,15 @@ WHERE UserId={userId}; INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp) VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0); -"); +" + ); } + return true; } public static decimal GetTotalCurrency(this DbSet users) - => users - .Sum((Func)(x => x.CurrencyAmount)); + => users.Sum((Func)(x => x.CurrencyAmount)); public static decimal GetTopOnePercentCurrency(this DbSet users, ulong botId) => users.AsQueryable() diff --git a/src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs b/src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs index e35cd38a7..d7dbbf024 100644 --- a/src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs +++ b/src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs @@ -12,7 +12,7 @@ public static class GuildConfigExtensions public ulong GuildId { get; set; } public ulong ChannelId { get; set; } } - + /// /// Gets full stream role settings for the guild with the specified id. /// @@ -21,31 +21,27 @@ public static class GuildConfigExtensions /// Guild'p stream role settings public static StreamRoleSettings GetStreamRoleSettings(this NadekoContext ctx, ulong guildId) { - var conf = ctx.GuildConfigsForId(guildId, set => set.Include(y => y.StreamRole) - .Include(y => y.StreamRole.Whitelist) - .Include(y => y.StreamRole.Blacklist)); + var conf = ctx.GuildConfigsForId(guildId, + set => set.Include(y => y.StreamRole) + .Include(y => y.StreamRole.Whitelist) + .Include(y => y.StreamRole.Blacklist) + ); if (conf.StreamRole is null) conf.StreamRole = new(); return conf.StreamRole; } - - private static List DefaultWarnPunishments => - new() { - new() { - Count = 3, - Punishment = PunishmentAction.Kick - }, - new() { - Count = 5, - Punishment = PunishmentAction.Ban - } + + private static List DefaultWarnPunishments + => new() + { + new() { Count = 3, Punishment = PunishmentAction.Kick }, + new() { Count = 5, Punishment = PunishmentAction.Ban } }; private static IQueryable IncludeEverything(this DbSet configs) - => configs - .AsQueryable() + => configs.AsQueryable() .AsSplitQuery() .Include(gc => gc.CommandCooldowns) .Include(gc => gc.FollowedStreams) @@ -56,9 +52,10 @@ public static class GuildConfigExtensions .Include(gc => gc.ReactionRoleMessages) .ThenInclude(x => x.ReactionRoles); - public static IEnumerable GetAllGuildConfigs(this DbSet configs, List availableGuilds) - => configs - .IncludeEverything() + public static IEnumerable GetAllGuildConfigs( + this DbSet configs, + List availableGuilds) + => configs.IncludeEverything() .AsNoTracking() .Where(x => availableGuilds.Contains(x.GuildId)) .ToList(); @@ -70,15 +67,16 @@ public static class GuildConfigExtensions /// Id of the guide /// Use to manipulate the set however you want. Pass null to include everything /// Config for the guild - public static GuildConfig GuildConfigsForId(this NadekoContext ctx, ulong guildId, Func, IQueryable> includes) + public static GuildConfig GuildConfigsForId( + this NadekoContext ctx, + ulong guildId, + Func, IQueryable> includes) { GuildConfig config; if (includes is null) { - config = ctx - .GuildConfigs - .IncludeEverything() + config = ctx.GuildConfigs.IncludeEverything() .FirstOrDefault(c => c.GuildId == guildId); } else @@ -90,12 +88,13 @@ public static class GuildConfigExtensions if (config is null) { ctx.GuildConfigs.Add(config = new() - { - GuildId = guildId, - Permissions = Permissionv2.GetDefaultPermlist, - WarningsInitialized = true, - WarnPunishments = DefaultWarnPunishments, - }); + { + GuildId = guildId, + Permissions = Permissionv2.GetDefaultPermlist, + WarningsInitialized = true, + WarnPunishments = DefaultWarnPunishments, + } + ); ctx.SaveChanges(); } @@ -110,21 +109,17 @@ public static class GuildConfigExtensions public static LogSetting LogSettingsFor(this NadekoContext ctx, ulong guildId) { - var logSetting = ctx.LogSettings - .AsQueryable() + var logSetting = ctx.LogSettings.AsQueryable() .Include(x => x.LogIgnores) .Where(x => x.GuildId == guildId) .FirstOrDefault(); if (logSetting is null) { - ctx.LogSettings.Add(logSetting = new () - { - GuildId = guildId - }); + ctx.LogSettings.Add(logSetting = new() { GuildId = guildId }); ctx.SaveChanges(); } - + return logSetting; } @@ -139,23 +134,18 @@ public static class GuildConfigExtensions public static GuildConfig GcWithPermissionsv2For(this NadekoContext ctx, ulong guildId) { - var config = ctx - .GuildConfigs - .AsQueryable() + var config = ctx.GuildConfigs.AsQueryable() .Where(gc => gc.GuildId == guildId) .Include(gc => gc.Permissions) .FirstOrDefault(); if (config is null) // if there is no guildconfig, create new one { - ctx.GuildConfigs.Add(config = new() - { - GuildId = guildId, - Permissions = Permissionv2.GetDefaultPermlist - }); + ctx.GuildConfigs.Add(config = new() { GuildId = guildId, Permissions = Permissionv2.GetDefaultPermlist }); ctx.SaveChanges(); } - else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones + else if (config.Permissions is null || + !config.Permissions.Any()) // if no perms, add default ones { config.Permissions = Permissionv2.GetDefaultPermlist; ctx.SaveChanges(); @@ -165,8 +155,7 @@ public static class GuildConfigExtensions } public static IEnumerable GetFollowedStreams(this DbSet configs) - => configs - .AsQueryable() + => configs.AsQueryable() .Include(x => x.FollowedStreams) .SelectMany(gc => gc.FollowedStreams) .ToArray(); @@ -196,7 +185,8 @@ public static class GuildConfigExtensions .Include(x => x.XpSettings) .ThenInclude(x => x.CurrencyRewards) .Include(x => x.XpSettings) - .ThenInclude(x => x.ExclusionList)); + .ThenInclude(x => x.ExclusionList) + ); if (gc.XpSettings is null) gc.XpSettings = new XpSettings(); @@ -205,15 +195,10 @@ public static class GuildConfigExtensions } public static IEnumerable GetGeneratingChannels(this DbSet configs) - => configs - .AsQueryable() + => configs.AsQueryable() .Include(x => x.GenerateCurrencyChannelIds) .Where(x => x.GenerateCurrencyChannelIds.Any()) .SelectMany(x => x.GenerateCurrencyChannelIds) - .Select(x => new GeneratingChannel() - { - ChannelId = x.ChannelId, - GuildId = x.GuildConfig.GuildId - }) + .Select(x => new GeneratingChannel() { ChannelId = x.ChannelId, GuildId = x.GuildConfig.GuildId }) .ToArray(); } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/MusicPlayerSettingsExtensions.cs b/src/NadekoBot/Db/Extensions/MusicPlayerSettingsExtensions.cs index e9d77600f..ba98938df 100644 --- a/src/NadekoBot/Db/Extensions/MusicPlayerSettingsExtensions.cs +++ b/src/NadekoBot/Db/Extensions/MusicPlayerSettingsExtensions.cs @@ -7,17 +7,12 @@ public static class MusicPlayerSettingsExtensions { public static async Task ForGuildAsync(this DbSet settings, ulong guildId) { - var toReturn = await settings - .AsQueryable() + var toReturn = await settings.AsQueryable() .FirstOrDefaultAsync(x => x.GuildId == guildId); if (toReturn is null) { - var newSettings = new MusicPlayerSettings() - { - GuildId = guildId, - PlayerRepeat = PlayerRepeatType.Queue - }; + var newSettings = new MusicPlayerSettings() { GuildId = guildId, PlayerRepeat = PlayerRepeatType.Queue }; await settings.AddAsync(newSettings); return newSettings; diff --git a/src/NadekoBot/Db/Extensions/MusicPlaylistExtensions.cs b/src/NadekoBot/Db/Extensions/MusicPlaylistExtensions.cs index b9b9b8158..40cd3cd9c 100644 --- a/src/NadekoBot/Db/Extensions/MusicPlaylistExtensions.cs +++ b/src/NadekoBot/Db/Extensions/MusicPlaylistExtensions.cs @@ -10,16 +10,14 @@ public static class MusicPlaylistExtensions if (num < 1) throw new IndexOutOfRangeException(); - return playlists - .AsQueryable() + return playlists.AsQueryable() .Skip((num - 1) * 20) .Take(20) .Include(pl => pl.Songs) .ToList(); } - public static MusicPlaylist GetWithSongs(this DbSet playlists, int id) => - playlists - .Include(mpl => mpl.Songs) + public static MusicPlaylist GetWithSongs(this DbSet playlists, int id) + => playlists.Include(mpl => mpl.Songs) .FirstOrDefault(mpl => mpl.Id == id); } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/PollExtensions.cs b/src/NadekoBot/Db/Extensions/PollExtensions.cs index 45a431c31..58fbd6e4d 100644 --- a/src/NadekoBot/Db/Extensions/PollExtensions.cs +++ b/src/NadekoBot/Db/Extensions/PollExtensions.cs @@ -13,27 +13,25 @@ public static class PollExtensions public static void RemovePoll(this NadekoContext ctx, int id) { - var p = ctx - .Poll - .Include(x => x.Answers) + var p = ctx.Poll.Include(x => x.Answers) .Include(x => x.Votes) .FirstOrDefault(x => x.Id == id); if (p is null) return; - + if (p.Votes != null) { ctx.RemoveRange(p.Votes); p.Votes.Clear(); } - + if (p.Answers != null) { ctx.RemoveRange(p.Answers); p.Answers.Clear(); } - + ctx.Poll.Remove(p); } } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/QuoteExtensions.cs b/src/NadekoBot/Db/Extensions/QuoteExtensions.cs index 0800056d2..b2edb003d 100644 --- a/src/NadekoBot/Db/Extensions/QuoteExtensions.cs +++ b/src/NadekoBot/Db/Extensions/QuoteExtensions.cs @@ -6,43 +6,58 @@ namespace NadekoBot.Db; public static class QuoteExtensions { public static IEnumerable GetForGuild(this DbSet quotes, ulong guildId) - => quotes.AsQueryable().Where(x => x.GuildId == guildId); + => quotes.AsQueryable() + .Where(x => x.GuildId == guildId); - public static IEnumerable GetGroup(this DbSet quotes, ulong guildId, int page, OrderType order) + public static IEnumerable GetGroup( + this DbSet quotes, + ulong guildId, + int page, + OrderType order) { - var q = quotes.AsQueryable().Where(x => x.GuildId == guildId); + var q = quotes.AsQueryable() + .Where(x => x.GuildId == guildId); if (order == OrderType.Keyword) q = q.OrderBy(x => x.Keyword); else q = q.OrderBy(x => x.Id); - return q.Skip(15 * page).Take(15).ToArray(); + return q.Skip(15 * page) + .Take(15) + .ToArray(); } - public static async Task GetRandomQuoteByKeywordAsync(this DbSet quotes, ulong guildId, string keyword) + public static async Task GetRandomQuoteByKeywordAsync( + this DbSet quotes, + ulong guildId, + string keyword) { var rng = new NadekoRandom(); return (await quotes.AsQueryable() .Where(q => q.GuildId == guildId && q.Keyword == keyword) - .ToListAsync()) - .OrderBy(q => rng.Next()) + .ToListAsync()).OrderBy(q => rng.Next()) .FirstOrDefault(); } - public static async Task SearchQuoteKeywordTextAsync(this DbSet quotes, ulong guildId, string keyword, string text) + public static async Task SearchQuoteKeywordTextAsync( + this DbSet quotes, + ulong guildId, + string keyword, + string text) { var rngk = new NadekoRandom(); return (await quotes.AsQueryable() - .Where(q => q.GuildId == guildId - && q.Keyword == keyword - && EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%") + .Where(q => q.GuildId == guildId && + q.Keyword == keyword && + EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%") // && q.Text.Contains(text, StringComparison.OrdinalIgnoreCase) ) - .ToListAsync()) - .OrderBy(q => rngk.Next()) + .ToListAsync()).OrderBy(q => rngk.Next()) .FirstOrDefault(); } public static void RemoveAllByKeyword(this DbSet quotes, ulong guildId, string keyword) - => quotes.RemoveRange(quotes.AsQueryable().Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword)); + => quotes.RemoveRange(quotes.AsQueryable() + .Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword) + ); } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/ReminderExtensions.cs b/src/NadekoBot/Db/Extensions/ReminderExtensions.cs index d26028b79..f1cb13060 100644 --- a/src/NadekoBot/Db/Extensions/ReminderExtensions.cs +++ b/src/NadekoBot/Db/Extensions/ReminderExtensions.cs @@ -5,7 +5,9 @@ namespace NadekoBot.Db; public static class ReminderExtensions { - public static IEnumerable GetIncludedReminders(this DbSet reminders, IEnumerable guildIds) + public static IEnumerable GetIncludedReminders( + this DbSet reminders, + IEnumerable guildIds) => reminders.AsQueryable() .Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0) .ToList(); @@ -16,7 +18,7 @@ public static class ReminderExtensions .OrderBy(x => x.DateAdded) .Skip(page * 10) .Take(10); - + public static IEnumerable RemindersForServer(this DbSet reminders, ulong serverId, int page) => reminders.AsQueryable() .Where(x => x.ServerId == serverId) diff --git a/src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs b/src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs index 9fa5eada0..a0a455c30 100644 --- a/src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs +++ b/src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs @@ -16,8 +16,8 @@ public static class SelfAssignableRolesExtensions return true; } - public static IEnumerable GetFromGuild(this DbSet roles, ulong guildId) - => roles.AsQueryable() + public static IEnumerable GetFromGuild(this DbSet roles, ulong guildId) + => roles.AsQueryable() .Where(s => s.GuildId == guildId) .ToArray(); } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/UserXpExtensions.cs b/src/NadekoBot/Db/Extensions/UserXpExtensions.cs index d2c87b63f..4d73f5f0a 100644 --- a/src/NadekoBot/Db/Extensions/UserXpExtensions.cs +++ b/src/NadekoBot/Db/Extensions/UserXpExtensions.cs @@ -14,20 +14,17 @@ public static class UserXpExtensions if (usr is null) { ctx.Add(usr = new() - { - Xp = 0, - UserId = userId, - NotifyOnLevelUp = XpNotificationLocation.None, - GuildId = guildId, - }); + { + Xp = 0, UserId = userId, NotifyOnLevelUp = XpNotificationLocation.None, GuildId = guildId, + } + ); } return usr; } public static List GetUsersFor(this DbSet xps, ulong guildId, int page) - => xps - .AsQueryable() + => xps.AsQueryable() .AsNoTracking() .Where(x => x.GuildId == guildId) .OrderByDescending(x => x.Xp + x.AwardedXp) @@ -36,8 +33,7 @@ public static class UserXpExtensions .ToList(); public static List GetTopUserXps(this DbSet xps, ulong guildId, int count) - => xps - .AsQueryable() + => xps.AsQueryable() .AsNoTracking() .Where(x => x.GuildId == guildId) .OrderByDescending(x => x.Xp + x.AwardedXp) @@ -51,15 +47,17 @@ public static class UserXpExtensions // FROM UserXpStats // WHERE UserId = @p2 AND GuildId = @p1 // LIMIT 1));"; - => xps - .AsQueryable() - .AsNoTracking() - .Where(x => x.GuildId == guildId && x.Xp + x.AwardedXp > - xps.AsQueryable() - .Where(y => y.UserId == userId && y.GuildId == guildId) - .Select(y => y.Xp + y.AwardedXp) - .FirstOrDefault()) - .Count() + 1; + => xps.AsQueryable() + .AsNoTracking() + .Where(x => x.GuildId == guildId && + x.Xp + x.AwardedXp > + xps.AsQueryable() + .Where(y => y.UserId == userId && y.GuildId == guildId) + .Select(y => y.Xp + y.AwardedXp) + .FirstOrDefault() + ) + .Count() + + 1; public static void ResetGuildUserXp(this DbSet xps, ulong userId, ulong guildId) => xps.Delete(x => x.UserId == userId && x.GuildId == guildId); diff --git a/src/NadekoBot/Db/Extensions/WaifuExtensions.cs b/src/NadekoBot/Db/Extensions/WaifuExtensions.cs index 6efbd9a16..8d8cf38e2 100644 --- a/src/NadekoBot/Db/Extensions/WaifuExtensions.cs +++ b/src/NadekoBot/Db/Extensions/WaifuExtensions.cs @@ -7,21 +7,24 @@ namespace NadekoBot.Db; public class WaifuInfoStats { - public string FullName { get; set; } - public int Price { get; set; } - public string ClaimerName { get; set; } - public string AffinityName { get; set; } - public int AffinityCount { get; set; } - public int DivorceCount { get; set; } - public int ClaimCount { get; set; } - public List Items { get; set; } - public List Claims { get; set; } - public List Fans { get; set; } + public string FullName { get; init; } + public int Price { get; init; } + public string ClaimerName { get; init; } + public string AffinityName { get; init; } + public int AffinityCount { get; init; } + public int DivorceCount { get; init; } + public int ClaimCount { get; init; } + public List Items { get; init; } + public List Claims { get; init; } + public List Fans { get; init; } } - + public static class WaifuExtensions { - public static WaifuInfo ByWaifuUserId(this DbSet waifus, ulong userId, Func, IQueryable> includes = null) + public static WaifuInfo ByWaifuUserId( + this DbSet waifus, + ulong userId, + Func, IQueryable> includes = null) { if (includes is null) { @@ -51,31 +54,28 @@ public static class WaifuExtensions .Skip(skip) .Take(count) .Select(x => new WaifuLbResult - { - Affinity = x.Affinity == null ? null : x.Affinity.Username, - AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator, - Claimer = x.Claimer == null ? null : x.Claimer.Username, - ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator, - Username = x.Waifu.Username, - Discrim = x.Waifu.Discriminator, - Price = x.Price, - }) + { + Affinity = x.Affinity == null ? null : x.Affinity.Username, + AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator, + Claimer = x.Claimer == null ? null : x.Claimer.Username, + ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator, + Username = x.Waifu.Username, + Discrim = x.Waifu.Discriminator, + Price = x.Price, + } + ) .ToList(); - } public static decimal GetTotalValue(this DbSet waifus) - => waifus - .AsQueryable() + => waifus.AsQueryable() .Where(x => x.ClaimerId != null) .Sum(x => x.Price); public static ulong GetWaifuUserId(this DbSet waifus, ulong ownerId, string name) - => waifus - .AsQueryable() + => waifus.AsQueryable() .AsNoTracking() - .Where(x => x.Claimer.UserId == ownerId - && x.Waifu.Username + "#" + x.Waifu.Discriminator == name) + .Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username + "#" + x.Waifu.Discriminator == name) .Select(x => x.Waifu.UserId) .FirstOrDefault(); @@ -83,74 +83,64 @@ public static class WaifuExtensions { ctx.Database.ExecuteSqlInterpolated($@" INSERT OR IGNORE INTO WaifuInfo (AffinityId, ClaimerId, Price, WaifuId) -VALUES ({null}, {null}, {1}, (SELECT Id FROM DiscordUser WHERE UserId={userId}));"); +VALUES ({null}, {null}, {1}, (SELECT Id FROM DiscordUser WHERE UserId={userId}));" + ); - var toReturn = ctx.WaifuInfo - .AsQueryable() - .Where(w => w.WaifuId == ctx.Set() - .AsQueryable() - .Where(u => u.UserId == userId) - .Select(u => u.Id).FirstOrDefault()) + var toReturn = ctx.WaifuInfo.AsQueryable() + .Where(w => w.WaifuId == + ctx.Set() + .AsQueryable() + .Where(u => u.UserId == userId) + .Select(u => u.Id) + .FirstOrDefault() + ) .Select(w => new WaifuInfoStats - { - FullName = ctx.Set() - .AsQueryable() - .Where(u => u.UserId == userId) - .Select(u => u.Username + "#" + u.Discriminator) - .FirstOrDefault(), - - AffinityCount = ctx.Set() - .AsQueryable() - .Count(x => x.UserId == w.WaifuId && - x.UpdateType == WaifuUpdateType.AffinityChanged && - x.NewId != null), - - AffinityName = ctx.Set() - .AsQueryable() - .Where(u => u.Id == w.AffinityId) - .Select(u => u.Username + "#" + u.Discriminator) - .FirstOrDefault(), - - ClaimCount = ctx.WaifuInfo - .AsQueryable() - .Count(x => x.ClaimerId == w.WaifuId), - - ClaimerName = ctx.Set() - .AsQueryable() - .Where(u => u.Id == w.ClaimerId) - .Select(u => u.Username + "#" + u.Discriminator) - .FirstOrDefault(), - - DivorceCount = ctx - .Set() - .AsQueryable() - .Count(x => x.OldId == w.WaifuId && - x.NewId == null && - x.UpdateType == WaifuUpdateType.Claimed), - - Price = w.Price, - - Claims = ctx.WaifuInfo - .AsQueryable() - .Include(x => x.Waifu) - .Where(x => x.ClaimerId == w.WaifuId) - .Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator) - .ToList(), - - Fans = ctx.WaifuInfo - .AsQueryable() - .Include(x => x.Waifu) - .Where(x => x.AffinityId == w.WaifuId) - .Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator) - .ToList(), - - Items = w.Items, - }) + { + FullName = ctx.Set() + .AsQueryable() + .Where(u => u.UserId == userId) + .Select(u => u.Username + "#" + u.Discriminator) + .FirstOrDefault(), + AffinityCount = ctx.Set() + .AsQueryable() + .Count(x => x.UserId == w.WaifuId && + x.UpdateType == WaifuUpdateType.AffinityChanged && + x.NewId != null + ), + AffinityName = ctx.Set() + .AsQueryable() + .Where(u => u.Id == w.AffinityId) + .Select(u => u.Username + "#" + u.Discriminator) + .FirstOrDefault(), + ClaimCount = ctx.WaifuInfo.AsQueryable() + .Count(x => x.ClaimerId == w.WaifuId), + ClaimerName = ctx.Set() + .AsQueryable() + .Where(u => u.Id == w.ClaimerId) + .Select(u => u.Username + "#" + u.Discriminator) + .FirstOrDefault(), + DivorceCount = ctx.Set() + .AsQueryable() + .Count(x => x.OldId == w.WaifuId && x.NewId == null && x.UpdateType == WaifuUpdateType.Claimed), + Price = w.Price, + Claims = ctx.WaifuInfo.AsQueryable() + .Include(x => x.Waifu) + .Where(x => x.ClaimerId == w.WaifuId) + .Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator) + .ToList(), + Fans = ctx.WaifuInfo.AsQueryable() + .Include(x => x.Waifu) + .Where(x => x.AffinityId == w.WaifuId) + .Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator) + .ToList(), + Items = w.Items, + } + ) .FirstOrDefault(); if (toReturn is null) return null; - + return toReturn; } } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/WarningExtensions.cs b/src/NadekoBot/Db/Extensions/WarningExtensions.cs index 3ef9c34ef..e0b33b471 100644 --- a/src/NadekoBot/Db/Extensions/WarningExtensions.cs +++ b/src/NadekoBot/Db/Extensions/WarningExtensions.cs @@ -14,17 +14,24 @@ public static class WarningExtensions return query.ToArray(); } - public static bool Forgive(this DbSet warnings, ulong guildId, ulong userId, string mod, int index) + public static bool Forgive( + this DbSet warnings, + ulong guildId, + ulong userId, + string mod, + int index) { if (index < 0) throw new ArgumentOutOfRangeException(nameof(index)); - var warn = warnings.AsQueryable().Where(x => x.GuildId == guildId && x.UserId == userId) + var warn = warnings.AsQueryable() + .Where(x => x.GuildId == guildId && x.UserId == userId) .OrderByDescending(x => x.DateAdded) .Skip(index) .FirstOrDefault(); - if (warn is null || warn.Forgiven) + if (warn is null || + warn.Forgiven) return false; warn.Forgiven = true; @@ -32,17 +39,25 @@ public static class WarningExtensions return true; } - public static async Task ForgiveAll(this DbSet warnings, ulong guildId, ulong userId, string mod) - => await warnings.AsQueryable().Where(x => x.GuildId == guildId && x.UserId == userId) + public static async Task ForgiveAll( + this DbSet warnings, + ulong guildId, + ulong userId, + string mod) + => await warnings.AsQueryable() + .Where(x => x.GuildId == guildId && x.UserId == userId) .ForEachAsync(x => - { - if (x.Forgiven != true) { - x.Forgiven = true; - x.ForgivenBy = mod; + if (x.Forgiven != true) + { + x.Forgiven = true; + x.ForgivenBy = mod; + } } - }); + ); public static Warning[] GetForGuild(this DbSet warnings, ulong id) - => warnings.AsQueryable().Where(x => x.GuildId == id).ToArray(); + => warnings.AsQueryable() + .Where(x => x.GuildId == id) + .ToArray(); } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/LocalizationCommands.cs index b7c4d21ba..940196f7a 100644 --- a/src/NadekoBot/Modules/Administration/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/LocalizationCommands.cs @@ -43,8 +43,8 @@ public partial class Administration [Priority(0)] public async Task LanguageSet() => await ReplyConfirmLocalizedAsync(strs.lang_set_show( - Format.Bold(_cultureInfo.ToString()), - Format.Bold(_cultureInfo.NativeName))); + Format.Bold(Culture.ToString()), + Format.Bold(Culture.NativeName))); [NadekoCommand, Aliases] [RequireContext(ContextType.Guild)] diff --git a/src/NadekoBot/Modules/Administration/Services/LogCommandService.cs b/src/NadekoBot/Modules/Administration/Services/LogCommandService.cs index 2c65a10a8..08db75ad8 100644 --- a/src/NadekoBot/Modules/Administration/Services/LogCommandService.cs +++ b/src/NadekoBot/Modules/Administration/Services/LogCommandService.cs @@ -1002,8 +1002,7 @@ public sealed class LogCommandService : ILogCommandService { try { - var msg = optMsg.Value as IUserMessage; - if (msg is null || msg.IsAuthor(_client)) + if (optMsg.Value is not IUserMessage msg || msg.IsAuthor(_client)) return; if (_ignoreMessageIds.Contains(msg.Id)) @@ -1058,8 +1057,7 @@ public sealed class LogCommandService : ILogCommandService if (imsg2 is not IUserMessage after || after.IsAuthor(_client)) return; - var before = (optmsg.HasValue ? optmsg.Value : null) as IUserMessage; - if (before is null) + if ((optmsg.HasValue ? optmsg.Value : null) is not IUserMessage before) return; if (ch is not ITextChannel channel) diff --git a/src/NadekoBot/Modules/Administration/Services/VcRoleService.cs b/src/NadekoBot/Modules/Administration/Services/VcRoleService.cs index 6c49d9275..34569a36c 100644 --- a/src/NadekoBot/Modules/Administration/Services/VcRoleService.cs +++ b/src/NadekoBot/Modules/Administration/Services/VcRoleService.cs @@ -167,9 +167,7 @@ public class VcRoleService : INService private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, SocketVoiceState newState) { - - var gusr = usr as SocketGuildUser; - if (gusr is null) + if (usr is not SocketGuildUser gusr) return Task.CompletedTask; var oldVc = oldState.VoiceChannel; diff --git a/src/NadekoBot/Modules/Administration/UserPunishCommands.cs b/src/NadekoBot/Modules/Administration/UserPunishCommands.cs index 4fe7616d8..3a89f65b3 100644 --- a/src/NadekoBot/Modules/Administration/UserPunishCommands.cs +++ b/src/NadekoBot/Modules/Administration/UserPunishCommands.cs @@ -465,7 +465,7 @@ public partial class Administration .AddField(GetText(strs.duration), time.Time.Humanize(3, minUnit: TimeUnit.Minute, - culture: _cultureInfo), + culture: Culture), true); if (dmFailed) diff --git a/src/NadekoBot/Modules/Gambling/Common/Events/ReactionEvent.cs b/src/NadekoBot/Modules/Gambling/Common/Events/ReactionEvent.cs index a55e0cc05..17079b1a1 100644 --- a/src/NadekoBot/Modules/Gambling/Common/Events/ReactionEvent.cs +++ b/src/NadekoBot/Modules/Gambling/Common/Events/ReactionEvent.cs @@ -152,8 +152,7 @@ public class ReactionEvent : ICurrencyEvent { if (_emote.Name != r.Emote.Name) return; - var gu = (r.User.IsSpecified ? r.User.Value : null) as IGuildUser; - if (gu is null // no unknown users, as they could be bots, or alts + if ((r.User.IsSpecified ? r.User.Value : null) is not IGuildUser gu // no unknown users, as they could be bots, or alts || msg.Id != _msg.Id // same message || gu.IsBot // no bots || (DateTime.UtcNow - gu.CreatedAt).TotalDays <= 5 // no recently created accounts diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index c1c0ff410..a33eb94ea 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -35,7 +35,7 @@ public partial class Gambling : GamblingModule private string n(long cur) { - var flowersCi = (CultureInfo)_cultureInfo.Clone(); + var flowersCi = (CultureInfo)Culture.Clone(); flowersCi.NumberFormat.CurrencySymbol = CurrencySign; Log.Information(string.Join(",", flowersCi.NumberFormat.NativeDigits)); return cur.ToString("C0", flowersCi); @@ -59,12 +59,12 @@ public partial class Gambling : GamblingModule } var embed = _eb.Create() .WithTitle(GetText(strs.economy_state)) - .AddField(GetText(strs.currency_owned), ((BigInteger)(ec.Cash - ec.Bot)).ToString("N", _cultureInfo) + CurrencySign) + .AddField(GetText(strs.currency_owned), ((BigInteger)(ec.Cash - ec.Bot)).ToString("N", Culture) + CurrencySign) .AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%") .AddField(GetText(strs.currency_planted), (BigInteger)ec.Planted) .AddField(GetText(strs.owned_waifus_total), (BigInteger)ec.Waifus + CurrencySign) .AddField(GetText(strs.bot_currency), n(ec.Bot)) - .AddField(GetText(strs.total), ((BigInteger)(ec.Cash + ec.Planted + ec.Waifus)).ToString("N", _cultureInfo) + CurrencySign) + .AddField(GetText(strs.total), ((BigInteger)(ec.Cash + ec.Planted + ec.Waifus)).ToString("N", Culture) + CurrencySign) .WithOkColor(); // ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Gambling/Services/CurrencyEventsService.cs b/src/NadekoBot/Modules/Gambling/Services/CurrencyEventsService.cs index 04fba6c41..42110f8a0 100644 --- a/src/NadekoBot/Modules/Gambling/Services/CurrencyEventsService.cs +++ b/src/NadekoBot/Modules/Gambling/Services/CurrencyEventsService.cs @@ -27,8 +27,7 @@ public class CurrencyEventsService : INService EventOptions opts, Func embed) { var g = _client.GetGuild(guildId); - var ch = g?.GetChannel(channelId) as SocketTextChannel; - if (ch is null) + if (g?.GetChannel(channelId) is not SocketTextChannel ch) return false; ICurrencyEvent ce; diff --git a/src/NadekoBot/Modules/Gambling/Services/PlantPickService.cs b/src/NadekoBot/Modules/Gambling/Services/PlantPickService.cs index 9afb2f49e..69a3b403c 100644 --- a/src/NadekoBot/Modules/Gambling/Services/PlantPickService.cs +++ b/src/NadekoBot/Modules/Gambling/Services/PlantPickService.cs @@ -159,8 +159,7 @@ public class PlantPickService : INService private Task PotentialFlowerGeneration(IUserMessage imsg) { - var msg = imsg as SocketUserMessage; - if (msg is null || msg.Author.IsBot) + if (imsg is not SocketUserMessage msg || msg.Author.IsBot) return Task.CompletedTask; if (imsg.Channel is not ITextChannel channel) diff --git a/src/NadekoBot/Modules/Games/Common/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Common/Trivia/TriviaGame.cs index 0f783e0dc..b67624084 100644 --- a/src/NadekoBot/Modules/Games/Common/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Common/Trivia/TriviaGame.cs @@ -196,8 +196,7 @@ public class TriviaGame var umsg = imsg as SocketUserMessage; - var textChannel = umsg?.Channel as ITextChannel; - if (textChannel is null || textChannel.Guild != Guild) + if (umsg?.Channel is not ITextChannel textChannel || textChannel.Guild != Guild) return; var guildUser = (IGuildUser)umsg.Author; diff --git a/src/NadekoBot/Modules/Games/Common/TypingGame.cs b/src/NadekoBot/Modules/Games/Common/TypingGame.cs index 37fa5dde3..a2bc19562 100644 --- a/src/NadekoBot/Modules/Games/Common/TypingGame.cs +++ b/src/NadekoBot/Modules/Games/Common/TypingGame.cs @@ -133,8 +133,7 @@ public class TypingGame { if (imsg.Author.IsBot) return; - var msg = imsg as SocketUserMessage; - if (msg is null) + if (imsg is not SocketUserMessage msg) return; if (this.Channel is null || this.Channel.Id != msg.Channel.Id) return; diff --git a/src/NadekoBot/Modules/Permissions/Services/FilterService.cs b/src/NadekoBot/Modules/Permissions/Services/FilterService.cs index 4c99d34f6..7c4d2b00e 100644 --- a/src/NadekoBot/Modules/Permissions/Services/FilterService.cs +++ b/src/NadekoBot/Modules/Permissions/Services/FilterService.cs @@ -97,9 +97,8 @@ public sealed class FilterService : IEarlyBehavior var _ = Task.Run(() => { var guild = (channel as ITextChannel)?.Guild; - var usrMsg = newMsg as IUserMessage; - if (guild is null || usrMsg is null) + if (guild is null || newMsg is not IUserMessage usrMsg) return Task.CompletedTask; return RunBehavior(guild, usrMsg); diff --git a/src/NadekoBot/Modules/Searches/PokemonSearchCommands.cs b/src/NadekoBot/Modules/Searches/PokemonSearchCommands.cs index 45089ced7..99ea5188c 100644 --- a/src/NadekoBot/Modules/Searches/PokemonSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/PokemonSearchCommands.cs @@ -56,7 +56,7 @@ public partial class Searches .WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc) ? kvp.Value.ShortDesc : kvp.Value.Desc) - .AddField(GetText(strs.rating), kvp.Value.Rating.ToString(_cultureInfo), true)); + .AddField(GetText(strs.rating), kvp.Value.Rating.ToString(Culture), true)); return; } } diff --git a/src/NadekoBot/Modules/Searches/Services/SearchesService.cs b/src/NadekoBot/Modules/Searches/Services/SearchesService.cs index de2df675b..935d1602a 100644 --- a/src/NadekoBot/Modules/Searches/Services/SearchesService.cs +++ b/src/NadekoBot/Modules/Searches/Services/SearchesService.cs @@ -626,9 +626,7 @@ public class SearchesService : INService var results = elems.Select(elem => { - var anchor = elem.QuerySelector(".result__a") as IHtmlAnchorElement; - - if (anchor is null) + if (elem.QuerySelector(".result__a") is not IHtmlAnchorElement anchor) return null; var href = anchor.Href; diff --git a/src/NadekoBot/Modules/Utility/RemindCommands.cs b/src/NadekoBot/Modules/Utility/RemindCommands.cs index 845e63990..86ce25dd9 100644 --- a/src/NadekoBot/Modules/Utility/RemindCommands.cs +++ b/src/NadekoBot/Modules/Utility/RemindCommands.cs @@ -127,7 +127,7 @@ public partial class Utility var diff = when - DateTime.UtcNow; embed.AddField( $"#{++i + (page * 10)} {rem.When:HH:mm yyyy-MM-dd} UTC " + - $"(in {diff.Humanize(2, minUnit: TimeUnit.Minute, culture: _cultureInfo)})", + $"(in {diff.Humanize(2, minUnit: TimeUnit.Minute, culture: Culture)})", $@"`Target:` {(rem.IsPrivate ? "DM" : "Channel")} `TargetId:` {rem.ChannelId} `Message:` {rem.Message?.TrimTo(50)}", false); @@ -230,7 +230,7 @@ public partial class Utility "⏰ " + GetText(strs.remind( Format.Bold(!isPrivate ? $"<#{targetId}>" : ctx.User.Username), Format.Bold(message), - ts.Humanize(3, minUnit: TimeUnit.Second, culture: _cultureInfo), + ts.Humanize(3, minUnit: TimeUnit.Second, culture: Culture), gTime, gTime))).ConfigureAwait(false); } catch diff --git a/src/NadekoBot/Modules/Xp/Xp.cs b/src/NadekoBot/Modules/Xp/Xp.cs index 4f783cf84..4e520ee88 100644 --- a/src/NadekoBot/Modules/Xp/Xp.cs +++ b/src/NadekoBot/Modules/Xp/Xp.cs @@ -106,14 +106,6 @@ public partial class Xp : NadekoModule }, allRewards.Count, 9); } - public enum AddRemove - { - Add = 0, - Remove = 1, - Rm = 1, - Rem = 1, - } - [NadekoCommand, Aliases] [UserPerm(GuildPerm.Administrator)] [BotPerm(GuildPerm.ManageRoles)] diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index 0bbf74f7c..4c4eb3346 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -8,7 +8,6 @@ $(MSBuildProjectDirectory) exe nadeko_icon.ico - true diff --git a/src/NadekoBot/Services/Impl/RedisImagesCache.cs b/src/NadekoBot/Services/Impl/RedisImagesCache.cs index 8730f1300..ce215b05c 100644 --- a/src/NadekoBot/Services/Impl/RedisImagesCache.cs +++ b/src/NadekoBot/Services/Impl/RedisImagesCache.cs @@ -7,16 +7,16 @@ namespace NadekoBot.Services; public sealed class RedisImagesCache : IImageCache, IReadyExecutor { - private readonly ConnectionMultiplexer _con; private readonly IBotCredentials _creds; private readonly HttpClient _http; private readonly string _imagesPath; - private IDatabase _db => _con.GetDatabase(); + private IDatabase Db + => _con.GetDatabase(); - private const string _basePath = "data/"; - private const string _cardsPath = $"{_basePath}images/cards"; + private const string BASE_PATH = "data/"; + private const string CARDS_PATH = $"{BASE_PATH}images/cards"; public ImageUrls ImageUrls { get; private set; } @@ -35,42 +35,42 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor XpBg } - public IReadOnlyList Heads + public IReadOnlyList Heads => GetByteArrayData(ImageKeys.CoinHeads); - public IReadOnlyList Tails + public IReadOnlyList Tails => GetByteArrayData(ImageKeys.CoinTails); - public IReadOnlyList Dice + public IReadOnlyList Dice => GetByteArrayData(ImageKeys.Dice); - public IReadOnlyList SlotEmojis + public IReadOnlyList SlotEmojis => GetByteArrayData(ImageKeys.SlotEmojis); - public IReadOnlyList Currency + public IReadOnlyList Currency => GetByteArrayData(ImageKeys.Currency); - public byte[] SlotBackground + public byte[] SlotBackground => GetByteData(ImageKeys.SlotBg); - public byte[] RategirlMatrix + public byte[] RategirlMatrix => GetByteData(ImageKeys.RategirlMatrix); - public byte[] RategirlDot + public byte[] RategirlDot => GetByteData(ImageKeys.RategirlDot); - public byte[] XpBackground + public byte[] XpBackground => GetByteData(ImageKeys.XpBg); - public byte[] Rip + public byte[] Rip => GetByteData(ImageKeys.RipBg); - public byte[] RipOverlay + public byte[] RipOverlay => GetByteData(ImageKeys.RipOverlay); public byte[] GetCard(string key) // since cards are always local for now, don't cache them - => File.ReadAllBytes(Path.Join(_cardsPath, key + ".jpg")); + => File.ReadAllBytes(Path.Join(CARDS_PATH, key + ".jpg")); public async Task OnReadyAsync() { @@ -85,7 +85,7 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor _con = con; _creds = creds; _http = new(); - _imagesPath = Path.Combine(_basePath, "images.yml"); + _imagesPath = Path.Combine(BASE_PATH, "images.yml"); Migrate(); @@ -95,58 +95,50 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor private void Migrate() { // migrate to yml - if (File.Exists(Path.Combine(_basePath, "images.json"))) + if (File.Exists(Path.Combine(BASE_PATH, "images.json"))) { - var oldFilePath = Path.Combine(_basePath, "images.json"); - var backupFilePath = Path.Combine(_basePath, "images.json.backup"); - - var oldData = JsonConvert.DeserializeObject( - File.ReadAllText(oldFilePath)); + var oldFilePath = Path.Combine(BASE_PATH, "images.json"); + var backupFilePath = Path.Combine(BASE_PATH, "images.json.backup"); + + var oldData = JsonConvert.DeserializeObject(File.ReadAllText(oldFilePath)); if (oldData is not null) { var newData = new ImageUrls() { - Coins = new() - { - Heads = oldData.Coins.Heads.Length == 1 && - oldData.Coins.Heads[0].ToString() == "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/heads.png" - ? new[] { new Uri("https://cdn.nadeko.bot/coins/heads3.png") } - : oldData.Coins.Heads, - Tails = oldData.Coins.Tails.Length == 1 && - oldData.Coins.Tails[0].ToString() == "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/tails.png" - ? new[] { new Uri("https://cdn.nadeko.bot/coins/tails3.png") } - : oldData.Coins.Tails, - }, + Coins = + new() + { + Heads = oldData.Coins.Heads.Length == 1 && + oldData.Coins.Heads[0].ToString() == + "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/heads.png" + ? new[] { new Uri("https://cdn.nadeko.bot/coins/heads3.png") } + : oldData.Coins.Heads, + Tails = oldData.Coins.Tails.Length == 1 && + oldData.Coins.Tails[0].ToString() == + "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/tails.png" + ? new[] { new Uri("https://cdn.nadeko.bot/coins/tails3.png") } + : oldData.Coins.Tails, + }, Dice = oldData.Dice.Map(x => x.ToNewCdn()), Currency = oldData.Currency.Map(x => x.ToNewCdn()), - Rategirl = new() - { - Dot = oldData.Rategirl.Dot.ToNewCdn(), - Matrix = oldData.Rategirl.Matrix.ToNewCdn() - }, - Rip = new() - { - Bg = oldData.Rip.Bg.ToNewCdn(), - Overlay = oldData.Rip.Overlay.ToNewCdn(), - }, + Rategirl = + new() + { + Dot = oldData.Rategirl.Dot.ToNewCdn(), Matrix = oldData.Rategirl.Matrix.ToNewCdn() + }, + Rip = new() { Bg = oldData.Rip.Bg.ToNewCdn(), Overlay = oldData.Rip.Overlay.ToNewCdn(), }, Slots = new() { Bg = new("https://cdn.nadeko.bot/slots/slots_bg.png"), Emojis = new[] { - "https://cdn.nadeko.bot/slots/0.png", - "https://cdn.nadeko.bot/slots/1.png", - "https://cdn.nadeko.bot/slots/2.png", - "https://cdn.nadeko.bot/slots/3.png", - "https://cdn.nadeko.bot/slots/4.png", - "https://cdn.nadeko.bot/slots/5.png" + "https://cdn.nadeko.bot/slots/0.png", "https://cdn.nadeko.bot/slots/1.png", + "https://cdn.nadeko.bot/slots/2.png", "https://cdn.nadeko.bot/slots/3.png", + "https://cdn.nadeko.bot/slots/4.png", "https://cdn.nadeko.bot/slots/5.png" }.Map(x => new Uri(x)) }, - Xp = new() - { - Bg = oldData.Xp.Bg.ToNewCdn(), - }, + Xp = new() { Bg = oldData.Xp.Bg.ToNewCdn(), }, Version = 2, }; @@ -216,25 +208,25 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor if (data is null) return; - await _db.StringSetAsync(GetRedisKey(key), data); + await Db.StringSetAsync(GetRedisKey(key), data); } private async Task Load(ImageKeys key, Uri[] uris) { - await _db.KeyDeleteAsync(GetRedisKey(key)); + await Db.KeyDeleteAsync(GetRedisKey(key)); var imageData = await Task.WhenAll(uris.Select(GetImageData)); - var vals = imageData - .Where(x => x is not null) - .Select(x => (RedisValue)x) - .ToArray(); + var vals = imageData.Where(x => x is not null).Select(x => (RedisValue)x).ToArray(); + + await Db.ListRightPushAsync(GetRedisKey(key), vals); - await _db.ListRightPushAsync(GetRedisKey(key), vals); - if (uris.Length != vals.Length) { Log.Information("{Loaded}/{Max} URIs for the key '{ImageKey}' have been loaded.\n" + - "Some of the supplied URIs are either unavailable or invalid.", - vals.Length, uris.Length, key); + "Some of the supplied URIs are either unavailable or invalid", + vals.Length, + uris.Length, + key + ); } } @@ -264,24 +256,23 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor return null; } } - + private async Task AllKeysExist() { - var tasks = await Task.WhenAll(GetAllKeys() - .Select(x => _db.KeyExistsAsync(GetRedisKey(x)))); + var tasks = await Task.WhenAll(GetAllKeys().Select(x => Db.KeyExistsAsync(GetRedisKey(x)))); return tasks.All(exist => exist); } - private IEnumerable GetAllKeys() => - Enum.GetValues(); + private IEnumerable GetAllKeys() + => Enum.GetValues(); private byte[][] GetByteArrayData(ImageKeys key) - => _db.ListRange(GetRedisKey(key)).Map(x => (byte[])x); + => Db.ListRange(GetRedisKey(key)).Map(x => (byte[])x); private byte[] GetByteData(ImageKeys key) - => _db.StringGet(GetRedisKey(key)); + => Db.StringGet(GetRedisKey(key)); - private RedisKey GetRedisKey(ImageKeys key) + private RedisKey GetRedisKey(ImageKeys key) => _creds.RedisKey() + "_image_" + key; } \ No newline at end of file diff --git a/src/NadekoBot/Services/Impl/RedisLocalDataCache.cs b/src/NadekoBot/Services/Impl/RedisLocalDataCache.cs index a2564648b..1146cef6f 100644 --- a/src/NadekoBot/Services/Impl/RedisLocalDataCache.cs +++ b/src/NadekoBot/Services/Impl/RedisLocalDataCache.cs @@ -10,35 +10,33 @@ public class RedisLocalDataCache : ILocalDataCache private readonly ConnectionMultiplexer _con; private readonly IBotCredentials _creds; - private IDatabase _db => _con.GetDatabase(); - - private const string pokemonAbilitiesFile = "data/pokemon/pokemon_abilities.json"; - private const string pokemonListFile = "data/pokemon/pokemon_list.json"; - private const string pokemonMapPath = "data/pokemon/name-id_map.json"; - private const string questionsFile = "data/trivia_questions.json"; + private const string POKEMON_ABILITIES_FILE = "data/pokemon/pokemon_abilities.json"; + private const string POKEMON_LIST_FILE = "data/pokemon/pokemon_list.json"; + private const string POKEMON_MAP_PATH = "data/pokemon/name-id_map.json"; + private const string QUESTIONS_FILE = "data/trivia_questions.json"; public IReadOnlyDictionary Pokemons { get => Get>("pokemon_list"); - private set => Set("pokemon_list", value); + private init => Set("pokemon_list", value); } public IReadOnlyDictionary PokemonAbilities { get => Get>("pokemon_abilities"); - private set => Set("pokemon_abilities", value); + private init => Set("pokemon_abilities", value); } public TriviaQuestion[] TriviaQuestions { get => Get("trivia_questions"); - private set => Set("trivia_questions", value); + private init => Set("trivia_questions", value); } public IReadOnlyDictionary PokemonMap { get => Get>("pokemon_map"); - private set => Set("pokemon_map", value); + private init => Set("pokemon_map", value); } public RedisLocalDataCache(ConnectionMultiplexer con, IBotCredentials creds, DiscordSocketClient client) @@ -49,29 +47,33 @@ public class RedisLocalDataCache : ILocalDataCache if (shardId == 0) { - if (!File.Exists(pokemonListFile)) + if (!File.Exists(POKEMON_LIST_FILE)) { - Log.Warning($"{pokemonListFile} is missing. Pokemon abilities not loaded"); + Log.Warning($"{POKEMON_LIST_FILE} is missing. Pokemon abilities not loaded"); } else { - Pokemons = JsonConvert.DeserializeObject>(File.ReadAllText(pokemonListFile)); + Pokemons = + JsonConvert.DeserializeObject>(File.ReadAllText(POKEMON_LIST_FILE)); } - if (!File.Exists(pokemonAbilitiesFile)) + if (!File.Exists(POKEMON_ABILITIES_FILE)) { - Log.Warning($"{pokemonAbilitiesFile} is missing. Pokemon abilities not loaded."); + Log.Warning($"{POKEMON_ABILITIES_FILE} is missing. Pokemon abilities not loaded."); } else { - PokemonAbilities = JsonConvert.DeserializeObject>(File.ReadAllText(pokemonAbilitiesFile)); + PokemonAbilities = + JsonConvert.DeserializeObject>( + File.ReadAllText(POKEMON_ABILITIES_FILE) + ); } try { - TriviaQuestions = JsonConvert.DeserializeObject(File.ReadAllText(questionsFile)); - PokemonMap = JsonConvert.DeserializeObject(File.ReadAllText(pokemonMapPath)) - .ToDictionary(x => x.Id, x => x.Name); + TriviaQuestions = JsonConvert.DeserializeObject(File.ReadAllText(QUESTIONS_FILE)); + PokemonMap = JsonConvert.DeserializeObject(File.ReadAllText(POKEMON_MAP_PATH)) + ?.ToDictionary(x => x.Id, x => x.Name) ?? new(); } catch (Exception ex) { @@ -81,9 +83,10 @@ public class RedisLocalDataCache : ILocalDataCache } } - private T Get(string key) where T : class - => JsonConvert.DeserializeObject(_db.StringGet($"{_creds.RedisKey()}_localdata_{key}")); + private T Get(string key) + where T : class + => JsonConvert.DeserializeObject(_con.GetDatabase().StringGet($"{_creds.RedisKey()}_localdata_{key}")); private void Set(string key, object obj) - => _db.StringSet($"{_creds.RedisKey()}_localdata_{key}", JsonConvert.SerializeObject(obj)); + => _con.GetDatabase().StringSet($"{_creds.RedisKey()}_localdata_{key}", JsonConvert.SerializeObject(obj)); } \ No newline at end of file diff --git a/src/NadekoBot/Common/Extensions/IBotCredentialsExtensions.cs b/src/NadekoBot/_Extensions/BotCredentialsExtensions.cs similarity index 100% rename from src/NadekoBot/Common/Extensions/IBotCredentialsExtensions.cs rename to src/NadekoBot/_Extensions/BotCredentialsExtensions.cs diff --git a/src/NadekoBot/Common/Extensions/ServiceCollectionExtensions.cs b/src/NadekoBot/_Extensions/ServiceCollectionExtensions.cs similarity index 87% rename from src/NadekoBot/Common/Extensions/ServiceCollectionExtensions.cs rename to src/NadekoBot/_Extensions/ServiceCollectionExtensions.cs index 8d40cfd1e..5b514e414 100644 --- a/src/NadekoBot/Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/NadekoBot/_Extensions/ServiceCollectionExtensions.cs @@ -11,8 +11,7 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddBotStringsServices(this IServiceCollection services, int totalShards) => totalShards <= 1 - ? services - .AddSingleton() + ? services.AddSingleton() .AddSingleton() .AddSingleton() : services.AddSingleton() @@ -25,7 +24,8 @@ public static class ServiceCollectionExtensions 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)); @@ -39,8 +39,7 @@ public static class ServiceCollectionExtensions => services.AddSealedSubclassesOf(typeof(IConfigMigrator)); public static IServiceCollection AddMusic(this IServiceCollection services) - => services - .AddSingleton() + => services.AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -49,14 +48,13 @@ public static class ServiceCollectionExtensions .AddSingleton() .AddSingleton() .AddSingleton(svc => svc.GetRequiredService()); - + // 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) {