diff --git a/Nadeko.Bot.Modules.Searches/Class1.cs b/Nadeko.Bot.Modules.Searches/Class1.cs deleted file mode 100644 index 9be3e7956..000000000 --- a/Nadeko.Bot.Modules.Searches/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Nadeko.Bot.Modules.Searches; - -public class Class1 -{ -} \ No newline at end of file diff --git a/Nadeko.Bot.Modules.Searches/Nadeko.Bot.Modules.Searches.csproj b/Nadeko.Bot.Modules.Searches/Nadeko.Bot.Modules.Searches.csproj deleted file mode 100644 index 6836c6808..000000000 --- a/Nadeko.Bot.Modules.Searches/Nadeko.Bot.Modules.Searches.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net7.0 - enable - enable - - - diff --git a/NadekoBot.sln b/NadekoBot.sln index 9d2391daf..556fe8503 100644 --- a/NadekoBot.sln +++ b/NadekoBot.sln @@ -25,7 +25,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Tests", "src\Nade EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Coordinator", "src\NadekoBot.Coordinator\NadekoBot.Coordinator.csproj", "{AE9B7F8C-81D7-4401-83A3-643B38258374}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Generators", "src\NadekoBot.Generators\NadekoBot.Generators.csproj", "{3BC3BDF8-1A0B-45EB-AB2B-C0891D4D37B8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nadeko.Bot.Generators.Strings", "src\Nadeko.Bot.Generators.Strings\Nadeko.Bot.Generators.Strings.csproj", "{3BC3BDF8-1A0B-45EB-AB2B-C0891D4D37B8}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.VotesApi", "src\NadekoBot.VotesApi\NadekoBot.VotesApi.csproj", "{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}" EndProject @@ -57,6 +57,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nadeko.Bot.Modules.Administ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nadeko.Bot.Modules.Help", "src\Nadeko.Bot.Modules.Help\Nadeko.Bot.Modules.Help.csproj", "{C6359697-25F1-4049-8D73-AC48043CA192}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nadeko.Bot.Modules.Patronage", "src\Nadeko.Bot.Modules.Patronage\Nadeko.Bot.Modules.Patronage.csproj", "{CE434517-926B-44FE-8449-FE34A4382267}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nadeko.Bot.Generators.Cloneable", "src\Nadeko.Bot.Generators.Cloneable\Nadeko.Bot.Generators.Cloneable.csproj", "{92770AF3-83EE-49F1-A0BB-79124D19A13D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -184,6 +188,18 @@ Global {C6359697-25F1-4049-8D73-AC48043CA192}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU {C6359697-25F1-4049-8D73-AC48043CA192}.Release|Any CPU.ActiveCfg = Release|Any CPU {C6359697-25F1-4049-8D73-AC48043CA192}.Release|Any CPU.Build.0 = Release|Any CPU + {CE434517-926B-44FE-8449-FE34A4382267}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE434517-926B-44FE-8449-FE34A4382267}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE434517-926B-44FE-8449-FE34A4382267}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU + {CE434517-926B-44FE-8449-FE34A4382267}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU + {CE434517-926B-44FE-8449-FE34A4382267}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE434517-926B-44FE-8449-FE34A4382267}.Release|Any CPU.Build.0 = Release|Any CPU + {92770AF3-83EE-49F1-A0BB-79124D19A13D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92770AF3-83EE-49F1-A0BB-79124D19A13D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92770AF3-83EE-49F1-A0BB-79124D19A13D}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU + {92770AF3-83EE-49F1-A0BB-79124D19A13D}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU + {92770AF3-83EE-49F1-A0BB-79124D19A13D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92770AF3-83EE-49F1-A0BB-79124D19A13D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -210,6 +226,8 @@ Global {A5B51533-33B6-43AF-9D2D-410613078E96} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2} {08C31FF8-AC47-498D-ACD9-612FA8001F1F} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2} {C6359697-25F1-4049-8D73-AC48043CA192} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2} + {CE434517-926B-44FE-8449-FE34A4382267} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2} + {92770AF3-83EE-49F1-A0BB-79124D19A13D} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5F3F555C-855F-4BE8-B526-D062D3E8ACA4} diff --git a/src/Nadeko.Bot.Common/Abstractions/strings/LocStr.cs b/src/Nadeko.Bot.Common/Abstractions/strings/LocStr.cs index 4f1822e87..e578e957b 100644 --- a/src/Nadeko.Bot.Common/Abstractions/strings/LocStr.cs +++ b/src/Nadeko.Bot.Common/Abstractions/strings/LocStr.cs @@ -1,13 +1,13 @@ -// namespace NadekoBot.Common; -// -// public readonly struct LocStr -// { -// public readonly string Key; -// public readonly object[] Params; -// -// public LocStr(string key, params object[] data) -// { -// Key = key; -// Params = data; -// } -// } \ No newline at end of file +namespace NadekoBot; + +public readonly struct LocStr +{ + public readonly string Key; + public readonly object[] Params; + + public LocStr(string key, params object[] data) + { + Key = key; + Params = data; + } +} \ No newline at end of file diff --git a/src/Nadeko.Bot.Common/Attributes/OnlyPublicBotAttribute.cs b/src/Nadeko.Bot.Common/Attributes/OnlyPublicBotAttribute.cs index e9400ec17..081ab8dbe 100644 --- a/src/Nadeko.Bot.Common/Attributes/OnlyPublicBotAttribute.cs +++ b/src/Nadeko.Bot.Common/Attributes/OnlyPublicBotAttribute.cs @@ -12,7 +12,7 @@ public sealed class OnlyPublicBotAttribute : PreconditionAttribute CommandInfo command, IServiceProvider services) { -#if GLOBAL_NADEKO || DEBUG +#if GLOBAL_NADEKO return Task.FromResult(PreconditionResult.FromSuccess()); #else return Task.FromResult(PreconditionResult.FromError("Only available on the public bot.")); diff --git a/src/Nadeko.Bot.Modules.Gambling/Gambling/Bank/IBankService.cs b/src/Nadeko.Bot.Common/Currency/IBankService.cs similarity index 100% rename from src/Nadeko.Bot.Modules.Gambling/Gambling/Bank/IBankService.cs rename to src/Nadeko.Bot.Common/Currency/IBankService.cs diff --git a/src/Nadeko.Bot.Common/IPermissionChecker.cs b/src/Nadeko.Bot.Common/IPermissionChecker.cs index 90c9d68ab..f41a898cd 100644 --- a/src/Nadeko.Bot.Common/IPermissionChecker.cs +++ b/src/Nadeko.Bot.Common/IPermissionChecker.cs @@ -11,5 +11,5 @@ public interface IPermissionChecker IMessageChannel channel, IUser author, string module, - string cmd); + string? cmd); } \ No newline at end of file diff --git a/src/Nadeko.Bot.Common/Nadeko.Bot.Common.csproj b/src/Nadeko.Bot.Common/Nadeko.Bot.Common.csproj index 86391348e..94651c6b1 100644 --- a/src/Nadeko.Bot.Common/Nadeko.Bot.Common.csproj +++ b/src/Nadeko.Bot.Common/Nadeko.Bot.Common.csproj @@ -7,7 +7,6 @@ - @@ -30,7 +29,8 @@ - + + diff --git a/src/Nadeko.Bot.Common/NadekoModule.cs b/src/Nadeko.Bot.Common/NadekoModule.cs index ab6830337..33c492fff 100644 --- a/src/Nadeko.Bot.Common/NadekoModule.cs +++ b/src/Nadeko.Bot.Common/NadekoModule.cs @@ -1,6 +1,5 @@ #nullable disable using System.Globalization; -using NadekoBot.Services; // ReSharper disable InconsistentNaming diff --git a/src/Nadeko.Bot.Common/Patronage/FeatureLimitKey.cs b/src/Nadeko.Bot.Common/Patronage/FeatureLimitKey.cs index 36780b0e2..17a42bc61 100644 --- a/src/Nadeko.Bot.Common/Patronage/FeatureLimitKey.cs +++ b/src/Nadeko.Bot.Common/Patronage/FeatureLimitKey.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public readonly struct FeatureLimitKey { diff --git a/src/Nadeko.Bot.Common/Patronage/FeatureQuotaStats.cs b/src/Nadeko.Bot.Common/Patronage/FeatureQuotaStats.cs index f3bbe4bfc..538088ef4 100644 --- a/src/Nadeko.Bot.Common/Patronage/FeatureQuotaStats.cs +++ b/src/Nadeko.Bot.Common/Patronage/FeatureQuotaStats.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public readonly struct FeatureQuotaStats { diff --git a/src/Nadeko.Bot.Common/Patronage/IPatronData.cs b/src/Nadeko.Bot.Common/Patronage/IPatronData.cs index 811beb7d8..f72d2238c 100644 --- a/src/Nadeko.Bot.Common/Patronage/IPatronData.cs +++ b/src/Nadeko.Bot.Common/Patronage/IPatronData.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; public interface ISubscriberData { diff --git a/src/Nadeko.Bot.Common/Patronage/IPatronageService.cs b/src/Nadeko.Bot.Common/Patronage/IPatronageService.cs index cbb6cac45..48eb061f8 100644 --- a/src/Nadeko.Bot.Common/Patronage/IPatronageService.cs +++ b/src/Nadeko.Bot.Common/Patronage/IPatronageService.cs @@ -1,7 +1,7 @@ using NadekoBot.Db.Models; using OneOf; -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; /// /// Manages patrons and provides access to their data diff --git a/src/Nadeko.Bot.Common/Patronage/ISubscriptionHandler.cs b/src/Nadeko.Bot.Common/Patronage/ISubscriptionHandler.cs index 7e2a49773..76f6d9593 100644 --- a/src/Nadeko.Bot.Common/Patronage/ISubscriptionHandler.cs +++ b/src/Nadeko.Bot.Common/Patronage/ISubscriptionHandler.cs @@ -1,5 +1,5 @@ #nullable disable -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; /// /// Services implementing this interface are handling pledges/subscriptions/payments coming diff --git a/src/Nadeko.Bot.Common/Patronage/Patron.cs b/src/Nadeko.Bot.Common/Patronage/Patron.cs index bfe928f04..1dfc5df86 100644 --- a/src/Nadeko.Bot.Common/Patronage/Patron.cs +++ b/src/Nadeko.Bot.Common/Patronage/Patron.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public readonly struct Patron { diff --git a/src/Nadeko.Bot.Common/Patronage/PatronConfigData.cs b/src/Nadeko.Bot.Common/Patronage/PatronConfigData.cs index 51f5794f9..223019eff 100644 --- a/src/Nadeko.Bot.Common/Patronage/PatronConfigData.cs +++ b/src/Nadeko.Bot.Common/Patronage/PatronConfigData.cs @@ -1,7 +1,7 @@ using NadekoBot.Common.Yml; using Cloneable; -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; [Cloneable] public partial class PatronConfigData : ICloneable diff --git a/src/Nadeko.Bot.Common/Patronage/PatronExtensions.cs b/src/Nadeko.Bot.Common/Patronage/PatronExtensions.cs index e41fc66b5..1b261fce1 100644 --- a/src/Nadeko.Bot.Common/Patronage/PatronExtensions.cs +++ b/src/Nadeko.Bot.Common/Patronage/PatronExtensions.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public static class PatronExtensions { diff --git a/src/Nadeko.Bot.Common/Patronage/PatronTier.cs b/src/Nadeko.Bot.Common/Patronage/PatronTier.cs index d9eb32a7c..2f6f64fa4 100644 --- a/src/Nadeko.Bot.Common/Patronage/PatronTier.cs +++ b/src/Nadeko.Bot.Common/Patronage/PatronTier.cs @@ -1,5 +1,5 @@ // ReSharper disable InconsistentNaming -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public enum PatronTier { diff --git a/src/Nadeko.Bot.Common/Patronage/QuotaLimit.cs b/src/Nadeko.Bot.Common/Patronage/QuotaLimit.cs index 79a6a1a58..ff4336376 100644 --- a/src/Nadeko.Bot.Common/Patronage/QuotaLimit.cs +++ b/src/Nadeko.Bot.Common/Patronage/QuotaLimit.cs @@ -1,6 +1,6 @@ using NadekoBot.Db.Models; -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; /// /// Represents information about why the user has triggered a quota limit diff --git a/src/Nadeko.Bot.Common/Patronage/QuotaPer.cs b/src/Nadeko.Bot.Common/Patronage/QuotaPer.cs index 9c7db6c34..a93d8979c 100644 --- a/src/Nadeko.Bot.Common/Patronage/QuotaPer.cs +++ b/src/Nadeko.Bot.Common/Patronage/QuotaPer.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public enum QuotaPer { diff --git a/src/Nadeko.Bot.Common/Patronage/SubscriptionChargeStatus.cs b/src/Nadeko.Bot.Common/Patronage/SubscriptionChargeStatus.cs index 6b4644b82..56d3d7152 100644 --- a/src/Nadeko.Bot.Common/Patronage/SubscriptionChargeStatus.cs +++ b/src/Nadeko.Bot.Common/Patronage/SubscriptionChargeStatus.cs @@ -1,5 +1,5 @@ #nullable disable -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; public enum SubscriptionChargeStatus { diff --git a/src/Nadeko.Bot.Common/Patronage/UserQuotaStats.cs b/src/Nadeko.Bot.Common/Patronage/UserQuotaStats.cs index 3d8387f53..d36f756c8 100644 --- a/src/Nadeko.Bot.Common/Patronage/UserQuotaStats.cs +++ b/src/Nadeko.Bot.Common/Patronage/UserQuotaStats.cs @@ -1,4 +1,4 @@ -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public readonly struct UserQuotaStats { diff --git a/src/Nadeko.Bot.Modules.Permisssions/Blacklist/BlacklistService.cs b/src/Nadeko.Bot.Common/Services/Impl/BlacklistService.cs similarity index 100% rename from src/Nadeko.Bot.Modules.Permisssions/Blacklist/BlacklistService.cs rename to src/Nadeko.Bot.Common/Services/Impl/BlacklistService.cs diff --git a/src/Nadeko.Bot.Common/Services/Impl/CommandsUtilityService.cs b/src/Nadeko.Bot.Common/Services/Impl/CommandsUtilityService.cs new file mode 100644 index 000000000..9102e28c4 --- /dev/null +++ b/src/Nadeko.Bot.Common/Services/Impl/CommandsUtilityService.cs @@ -0,0 +1,172 @@ +using CommandLine; + +namespace NadekoBot.Common; + +public sealed class CommandsUtilityService : ICommandsUtilityService, INService +{ + private readonly CommandHandler _ch; + private readonly IBotStrings _strings; + private readonly DiscordPermOverrideService _dpos; + private readonly IEmbedBuilderService _eb; + private readonly ILocalization _loc; + private readonly Nadeko.Medusa.IMedusaLoaderService _medusae; + + public CommandsUtilityService( + CommandHandler ch, + IBotStrings strings, + DiscordPermOverrideService dpos, + IEmbedBuilderService eb, + ILocalization loc, + Nadeko.Medusa.IMedusaLoaderService medusae) + { + _ch = ch; + _strings = strings; + _dpos = dpos; + _eb = eb; + _loc = loc; + _medusae = medusae; + } + + public IEmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild) + { + var prefix = _ch.GetPrefix(guild); + + var str = $"**`{prefix + com.Aliases.First()}`**"; + var alias = com.Aliases.Skip(1).FirstOrDefault(); + if (alias is not null) + str += $" **/ `{prefix + alias}`**"; + + var culture = _loc.GetCultureInfo(guild); + + var em = _eb.Create() + .AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true); + + _dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides); + var reqs = GetCommandRequirements(com, (GuildPermission?)overrides); + if (reqs.Any()) + em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs)); + + em.AddField(_strings.GetText(strs.usage), + string.Join("\n", com.RealRemarksArr(_strings, _medusae, culture, prefix).Map(arg => Format.Code(arg)))) + .WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild)) + .WithOkColor(); + + var opt = GetNadekoOptionType(com.Attributes); + if (opt is not null) + { + var hs = GetCommandOptionHelp(opt); + if (!string.IsNullOrWhiteSpace(hs)) + em.AddField(GetText(strs.options, guild), hs); + } + + return em; + } + + public static string GetCommandOptionHelp(Type opt) + { + var strs = GetCommandOptionHelpList(opt); + + return string.Join("\n", strs); + } + + public static List GetCommandOptionHelpList(Type opt) + { + var strs = opt.GetProperties() + .Select(x => x.GetCustomAttributes(true).FirstOrDefault(a => a is OptionAttribute)) + .Where(x => x is not null) + .Cast() + .Select(x => + { + var toReturn = $"`--{x.LongName}`"; + + if (!string.IsNullOrWhiteSpace(x.ShortName)) + toReturn += $" (`-{x.ShortName}`)"; + + toReturn += $" {x.HelpText} "; + return toReturn; + }) + .ToList(); + + return strs; + } + + public static Type GetNadekoOptionType(IEnumerable attributes) + => attributes + .Select(a => a.GetType()) + .Where(a => a.IsGenericType + && a.GetGenericTypeDefinition() == typeof(NadekoOptionsAttribute<>)) + .Select(a => a.GenericTypeArguments[0]) + .FirstOrDefault(); + + public static string[] GetCommandRequirements(CommandInfo cmd, GuildPerm? overrides = null) + { + var toReturn = new List(); + + if (cmd.Preconditions.Any(x => x is OwnerOnlyAttribute)) + toReturn.Add("Bot Owner Only"); + + if (cmd.Preconditions.Any(x => x is NoPublicBotAttribute) + || cmd.Module + .Preconditions + .Any(x => x is NoPublicBotAttribute) + || cmd.Module.GetTopLevelModule() + .Preconditions + .Any(x => x is NoPublicBotAttribute)) + toReturn.Add("No Public Bot"); + + if (cmd.Preconditions + .Any(x => x is OnlyPublicBotAttribute) + || cmd.Module + .Preconditions + .Any(x => x is OnlyPublicBotAttribute) + || cmd.Module.GetTopLevelModule() + .Preconditions + .Any(x => x is OnlyPublicBotAttribute)) + toReturn.Add("Only Public Bot"); + + var userPermString = cmd.Preconditions + .Where(ca => ca is UserPermAttribute) + .Cast() + .Select(userPerm => + { + if (userPerm.ChannelPermission is { } cPerm) + return GetPreconditionString(cPerm); + + if (userPerm.GuildPermission is { } gPerm) + return GetPreconditionString(gPerm); + + return string.Empty; + }) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Join('\n'); + + if (overrides is null) + { + if (!string.IsNullOrWhiteSpace(userPermString)) + toReturn.Add(userPermString); + } + else + { + if (!string.IsNullOrWhiteSpace(userPermString)) + toReturn.Add(Format.Strikethrough(userPermString)); + + toReturn.Add(GetPreconditionString(overrides.Value)); + } + + return toReturn.ToArray(); + } + + public static string GetPreconditionString(ChannelPerm perm) + => (perm + " Channel Permission").Replace("Guild", "Server"); + + public static string GetPreconditionString(GuildPerm perm) + => (perm + " Server Permission").Replace("Guild", "Server"); + + public string GetText(LocStr str, IGuild? guild) + => _strings.GetText(str, guild?.Id); +} + +public interface ICommandsUtilityService +{ + IEmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild); +} \ No newline at end of file diff --git a/src/Nadeko.Bot.Modules.Administration/PermOverrides/DiscordPermOverrideService.cs b/src/Nadeko.Bot.Common/Services/Impl/DiscordPermOverrideService.cs similarity index 98% rename from src/Nadeko.Bot.Modules.Administration/PermOverrides/DiscordPermOverrideService.cs rename to src/Nadeko.Bot.Common/Services/Impl/DiscordPermOverrideService.cs index 27bbe0aa1..dcc36b354 100644 --- a/src/Nadeko.Bot.Modules.Administration/PermOverrides/DiscordPermOverrideService.cs +++ b/src/Nadeko.Bot.Common/Services/Impl/DiscordPermOverrideService.cs @@ -4,7 +4,7 @@ using Nadeko.Common; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Services.Database.Models; -namespace NadekoBot.Modules.Administration.Services; +namespace NadekoBot.Services; public class DiscordPermOverrideService : INService, IExecPreCommand, IDiscordPermOverrideService { diff --git a/src/Nadeko.Bot.Db/Nadeko.Bot.Db.csproj b/src/Nadeko.Bot.Db/Nadeko.Bot.Db.csproj index 4453a756b..0921b5ce7 100644 --- a/src/Nadeko.Bot.Db/Nadeko.Bot.Db.csproj +++ b/src/Nadeko.Bot.Db/Nadeko.Bot.Db.csproj @@ -9,16 +9,16 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/src/Nadeko.Bot.Generators.Cloneable/CloneableGenerator.cs b/src/Nadeko.Bot.Generators.Cloneable/CloneableGenerator.cs index 0c583fb1b..45392d16b 100644 --- a/src/Nadeko.Bot.Generators.Cloneable/CloneableGenerator.cs +++ b/src/Nadeko.Bot.Generators.Cloneable/CloneableGenerator.cs @@ -6,8 +6,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -using System.Collections.Generic; -using System.Linq; using System.Text; namespace Cloneable @@ -23,54 +21,60 @@ namespace Cloneable private const string CLONE_ATTRIBUTE_STRING = "CloneAttribute"; private const string IGNORE_CLONE_ATTRIBUTE_STRING = "IgnoreCloneAttribute"; - private const string CLONEABLE_ATTRIBUTE_TEXT = @"// -using System; + private const string CLONEABLE_ATTRIBUTE_TEXT = $$""" + // + using System; + + namespace {{CLONEABLE_NAMESPACE}} + { + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = false)] + internal sealed class {{CLONEABLE_ATTRIBUTE_STRING}} : Attribute + { + public {{CLONEABLE_ATTRIBUTE_STRING}}() + { + } + + public bool {{EXPLICIT_DECLARATION_KEY_STRING}} { get; set; } + } + } -namespace " + CLONEABLE_NAMESPACE + @" -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = false)] - public sealed class " + CLONEABLE_ATTRIBUTE_STRING + @" : Attribute - { - public " + CLONEABLE_ATTRIBUTE_STRING + @"() - { - } + """; - public bool " + EXPLICIT_DECLARATION_KEY_STRING + @" { get; set; } - } -} -"; + private const string CLONE_PROPERTY_ATTRIBUTE_TEXT = $$""" + // + using System; + + namespace {{CLONEABLE_NAMESPACE}} + { + [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] + internal sealed class {{CLONE_ATTRIBUTE_STRING}} : Attribute + { + public {{CLONE_ATTRIBUTE_STRING}}() + { + } + + public bool {{PREVENT_DEEP_COPY_KEY_STRING}} { get; set; } + } + } - private const string CLONE_PROPERTY_ATTRIBUTE_TEXT = @"// -using System; + """; -namespace " + CLONEABLE_NAMESPACE + @" -{ - [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] - public sealed class " + CLONE_ATTRIBUTE_STRING + @" : Attribute - { - public " + CLONE_ATTRIBUTE_STRING + @"() - { - } + private const string IGNORE_CLONE_PROPERTY_ATTRIBUTE_TEXT = $$""" + // + using System; + + namespace {{CLONEABLE_NAMESPACE}} + { + [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] + internal sealed class {{IGNORE_CLONE_ATTRIBUTE_STRING}} : Attribute + { + public {{IGNORE_CLONE_ATTRIBUTE_STRING}}() + { + } + } + } - public bool " + PREVENT_DEEP_COPY_KEY_STRING + @" { get; set; } - } -} -"; - - private const string IGNORE_CLONE_PROPERTY_ATTRIBUTE_TEXT = @"// -using System; - -namespace " + CLONEABLE_NAMESPACE + @" -{ - [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] - public sealed class " + IGNORE_CLONE_ATTRIBUTE_STRING + @" : Attribute - { - public " + IGNORE_CLONE_ATTRIBUTE_STRING + @"() - { - } - } -} -"; + """; private INamedTypeSymbol? _cloneableAttribute; private INamedTypeSymbol? _ignoreCloneAttribute; diff --git a/src/Nadeko.Bot.Generators.Cloneable/Nadeko.Bot.Generators.Cloneable.csproj b/src/Nadeko.Bot.Generators.Cloneable/Nadeko.Bot.Generators.Cloneable.csproj index 6836c6808..5ba6445d2 100644 --- a/src/Nadeko.Bot.Generators.Cloneable/Nadeko.Bot.Generators.Cloneable.csproj +++ b/src/Nadeko.Bot.Generators.Cloneable/Nadeko.Bot.Generators.Cloneable.csproj @@ -1,9 +1,17 @@ - net7.0 + netstandard2.0 + latest + false + true + true enable enable + + + + diff --git a/src/NadekoBot.Generators/LocalizedStringsGenerator.cs b/src/Nadeko.Bot.Generators.Strings/LocalizedStringsGenerator.cs similarity index 89% rename from src/NadekoBot.Generators/LocalizedStringsGenerator.cs rename to src/Nadeko.Bot.Generators.Strings/LocalizedStringsGenerator.cs index 52d2491bd..4fe5cce09 100644 --- a/src/NadekoBot.Generators/LocalizedStringsGenerator.cs +++ b/src/Nadeko.Bot.Generators.Strings/LocalizedStringsGenerator.cs @@ -26,20 +26,20 @@ namespace NadekoBot.Generators [Generator] public class LocalizedStringsGenerator : ISourceGenerator { - private const string LOC_STR_SOURCE = @"namespace NadekoBot -{ - public readonly struct LocStr - { - public readonly string Key; - public readonly object[] Params; - - public LocStr(string key, params object[] data) - { - Key = key; - Params = data; - } - } -}"; +// private const string LOC_STR_SOURCE = @"namespace NadekoBot +// { +// public readonly struct LocStr +// { +// public readonly string Key; +// public readonly object[] Params; +// +// public LocStr(string key, params object[] data) +// { +// Key = key; +// Params = data; +// } +// } +// }"; public void Initialize(GeneratorInitializationContext context) { @@ -106,7 +106,7 @@ namespace NadekoBot.Generators context.AddSource("strs.g.cs", stringWriter.ToString()); } - context.AddSource("LocStr.g.cs", LOC_STR_SOURCE); + // context.AddSource("LocStr.g.cs", LOC_STR_SOURCE); } private List GetFields(string? dataText) diff --git a/src/NadekoBot.Generators/NadekoBot.Generators.csproj b/src/Nadeko.Bot.Generators.Strings/Nadeko.Bot.Generators.Strings.csproj similarity index 88% rename from src/NadekoBot.Generators/NadekoBot.Generators.csproj rename to src/Nadeko.Bot.Generators.Strings/Nadeko.Bot.Generators.Strings.csproj index de4842b04..c49280b68 100644 --- a/src/NadekoBot.Generators/NadekoBot.Generators.csproj +++ b/src/Nadeko.Bot.Generators.Strings/Nadeko.Bot.Generators.Strings.csproj @@ -6,6 +6,9 @@ false true true + enable + enable + NadekoBot.Generators diff --git a/src/NadekoBot.Generators/README.md b/src/Nadeko.Bot.Generators.Strings/README.md similarity index 100% rename from src/NadekoBot.Generators/README.md rename to src/Nadeko.Bot.Generators.Strings/README.md diff --git a/src/Nadeko.Bot.Modules.Administration/Role/IReactionRoleService.cs b/src/Nadeko.Bot.Modules.Administration/Role/IReactionRoleService.cs index cdd5a51a8..3966d91bd 100644 --- a/src/Nadeko.Bot.Modules.Administration/Role/IReactionRoleService.cs +++ b/src/Nadeko.Bot.Modules.Administration/Role/IReactionRoleService.cs @@ -1,5 +1,5 @@ #nullable disable -using NadekoBot.Modules.Utility.Patronage; +using NadekoBot.Modules.Patronage; using NadekoBot.Services.Database.Models; using OneOf; using OneOf.Types; diff --git a/src/Nadeko.Bot.Modules.Administration/Role/ReactionRolesService.cs b/src/Nadeko.Bot.Modules.Administration/Role/ReactionRolesService.cs index b70e4ec97..9b35ab141 100644 --- a/src/Nadeko.Bot.Modules.Administration/Role/ReactionRolesService.cs +++ b/src/Nadeko.Bot.Modules.Administration/Role/ReactionRolesService.cs @@ -3,7 +3,7 @@ using LinqToDB; using LinqToDB.EntityFrameworkCore; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Db; -using NadekoBot.Modules.Utility.Patronage; +using NadekoBot.Modules.Patronage; using NadekoBot.Services.Database.Models; using OneOf.Types; using OneOf; diff --git a/src/Nadeko.Bot.Modules.Expresssions/Nadeko.Bot.Modules.Expresssions.csproj b/src/Nadeko.Bot.Modules.Expresssions/Nadeko.Bot.Modules.Expresssions.csproj index 4dd08788e..49b978351 100644 --- a/src/Nadeko.Bot.Modules.Expresssions/Nadeko.Bot.Modules.Expresssions.csproj +++ b/src/Nadeko.Bot.Modules.Expresssions/Nadeko.Bot.Modules.Expresssions.csproj @@ -18,8 +18,4 @@ - - - - diff --git a/src/Nadeko.Bot.Modules.Gambling/Gambling/Gambling.cs b/src/Nadeko.Bot.Modules.Gambling/Gambling/Gambling.cs index 136ab7f36..dc8cb7102 100644 --- a/src/Nadeko.Bot.Modules.Gambling/Gambling/Gambling.cs +++ b/src/Nadeko.Bot.Modules.Gambling/Gambling/Gambling.cs @@ -3,7 +3,6 @@ using LinqToDB; using LinqToDB.EntityFrameworkCore; using NadekoBot.Db; using NadekoBot.Db.Models; -using NadekoBot.Modules.Utility.Patronage; using NadekoBot.Modules.Gambling.Bank; using NadekoBot.Modules.Gambling.Common; using NadekoBot.Modules.Gambling.Services; @@ -15,6 +14,7 @@ using System.Globalization; using System.Text; using Nadeko.Econ.Gambling.Rps; using NadekoBot.Common.TypeReaders; +using NadekoBot.Modules.Patronage; namespace NadekoBot.Modules.Gambling; diff --git a/src/Nadeko.Bot.Modules.Gambling/Games/ChatterBot/ChatterbotService.cs b/src/Nadeko.Bot.Modules.Gambling/Games/ChatterBot/ChatterbotService.cs index e5e11e0a8..5910d8ce8 100644 --- a/src/Nadeko.Bot.Modules.Gambling/Games/ChatterBot/ChatterbotService.cs +++ b/src/Nadeko.Bot.Modules.Gambling/Games/ChatterBot/ChatterbotService.cs @@ -4,8 +4,8 @@ using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Db.Models; using NadekoBot.Modules.Games.Common; using NadekoBot.Modules.Games.Common.ChatterBot; +using NadekoBot.Modules.Patronage; using NadekoBot.Modules.Permissions; -using NadekoBot.Modules.Utility.Patronage; namespace NadekoBot.Modules.Games.Services; diff --git a/src/Nadeko.Bot.Modules.Gambling/Nadeko.Bot.Modules.Gambling.csproj b/src/Nadeko.Bot.Modules.Gambling/Nadeko.Bot.Modules.Gambling.csproj index 1a78c0607..ff82a6739 100644 --- a/src/Nadeko.Bot.Modules.Gambling/Nadeko.Bot.Modules.Gambling.csproj +++ b/src/Nadeko.Bot.Modules.Gambling/Nadeko.Bot.Modules.Gambling.csproj @@ -7,13 +7,15 @@ - - + + + + - - + + diff --git a/src/Nadeko.Bot.Modules.Help/Help.cs b/src/Nadeko.Bot.Modules.Help/Help.cs index e52a865d5..7c71a9606 100644 --- a/src/Nadeko.Bot.Modules.Help/Help.cs +++ b/src/Nadeko.Bot.Modules.Help/Help.cs @@ -6,18 +6,20 @@ using NadekoBot.Modules.Help.Services; using Newtonsoft.Json; using System.Text; using System.Text.Json; +using Nadeko.Bot.Common; using JsonSerializer = System.Text.Json.JsonSerializer; namespace NadekoBot.Modules.Help; -public partial class Help : NadekoModule +public sealed class Help : NadekoModule { public const string PATREON_URL = "https://patreon.com/nadekobot"; public const string PAYPAL_URL = "https://paypal.me/Kwoth"; + private readonly ICommandsUtilityService _cus; private readonly CommandService _cmds; private readonly BotConfigService _bss; - private readonly GlobalPermissionService _perms; + private readonly IPermissionChecker _perms; private readonly IServiceProvider _services; private readonly DiscordSocketClient _client; private readonly IBotStrings _strings; @@ -26,7 +28,8 @@ public partial class Help : NadekoModule private readonly IMedusaLoaderService _medusae; public Help( - GlobalPermissionService perms, + ICommandsUtilityService _cus, + IPermissionChecker perms, CommandService cmds, BotConfigService bss, IServiceProvider services, @@ -34,6 +37,7 @@ public partial class Help : NadekoModule IBotStrings strings, IMedusaLoaderService medusae) { + this._cus = _cus; _cmds = cmds; _bss = bss; _perms = perms; @@ -53,11 +57,11 @@ public partial class Help : NadekoModule var clientId = await _lazyClientId.Value; var r = new ReplacementBuilder().WithDefault(Context) - .WithOverride("{0}", () => clientId.ToString()) - .WithOverride("{1}", () => prefix) - .WithOverride("%prefix%", () => prefix) - .WithOverride("%bot.prefix%", () => prefix) - .Build(); + .WithOverride("{0}", () => clientId.ToString()) + .WithOverride("{1}", () => prefix) + .WithOverride("%prefix%", () => prefix) + .WithOverride("%bot.prefix%", () => prefix) + .Build(); var text = SmartText.CreateFrom(botSettings.HelpText); return r.Replace(text); @@ -69,10 +73,15 @@ public partial class Help : NadekoModule if (--page < 0) return; - var topLevelModules = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()) - .Where(m => !_perms.BlockedModules.Contains(m.Key.Name.ToLowerInvariant())) - .Select(x => x.Key) - .ToList(); + var topLevelModules = new List(); + foreach (var m in _cmds.Modules.GroupBy(x => x.GetTopLevelModule()).Select(x => x.Key)) + { + var result = await _perms.CheckAsync(ctx.Guild, ctx.Channel, ctx.User, + m.Name, null); + + if (result.IsT0) + topLevelModules.Add(m); + } await ctx.SendPaginatedConfirmAsync(page, cur => @@ -88,12 +97,12 @@ public partial class Help : NadekoModule } localModules.OrderBy(module => module.Name) - .ToList() - .ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}", - GetModuleDescription(module.Name) - + "\n" - + Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))), - true)); + .ToList() + .ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}", + GetModuleDescription(module.Name) + + "\n" + + Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))), + true)); return embed; }, @@ -109,11 +118,11 @@ public partial class Help : NadekoModule if (key.Key == strs.module_description_missing.Key) { var desc = _medusae - .GetLoadedMedusae(Culture) - .FirstOrDefault(m => m.Sneks - .Any(x => x.Name.Equals(moduleName, - StringComparison.InvariantCultureIgnoreCase))) - ?.Description; + .GetLoadedMedusae(Culture) + .FirstOrDefault(m => m.Sneks + .Any(x => x.Name.Equals(moduleName, + StringComparison.InvariantCultureIgnoreCase))) + ?.Description; if (desc is not null) return desc; @@ -150,6 +159,8 @@ public partial class Help : NadekoModule return strs.module_description_xp; case "medusa": return strs.module_description_medusa; + case "patronage": + return strs.module_description_patronage; default: return strs.module_description_missing; } @@ -182,6 +193,8 @@ public partial class Help : NadekoModule return "🚓"; case "xp": return "📝"; + case "patronage": + return "💝"; default: return "📖"; } @@ -191,7 +204,6 @@ public partial class Help : NadekoModule [NadekoOptions] public async Task Commands(string module = null, params string[] args) { - module = module?.Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(module)) { await Modules(); @@ -203,14 +215,22 @@ public partial class Help : NadekoModule // Find commands for that module // don't show commands which are blocked // order by name - var cmds = _cmds.Commands - .Where(c => c.Module.GetTopLevelModule() - .Name.ToUpperInvariant() - .StartsWith(module, StringComparison.InvariantCulture)) - .Where(c => !_perms.BlockedCommands.Contains(c.Aliases[0].ToLowerInvariant())) - .OrderBy(c => c.Aliases[0]) - .DistinctBy(x => x.Aliases[0]) - .ToList(); + var allowed = new List(); + + foreach (var cmd in _cmds.Commands + .Where(c => c.Module.GetTopLevelModule() + .Name + .StartsWith(module, StringComparison.InvariantCultureIgnoreCase))) + { + var result = await _perms.CheckAsync(ctx.Guild, ctx.Channel, ctx.User, cmd.Module.GetTopLevelModule().Name, + cmd.Name); + if (result.IsT0) + allowed.Add(cmd); + } + + var cmds = allowed.OrderBy(c => c.Aliases[0]) + .DistinctBy(x => x.Aliases[0]) + .ToList(); // check preconditions for all commands, but only if it's not 'all' @@ -219,12 +239,12 @@ public partial class Help : NadekoModule if (opts.View != CommandsOptions.ViewType.All) { succ = new((await cmds.Select(async x => - { - var pre = await x.CheckPreconditionsAsync(Context, _services); - return (Cmd: x, Succ: pre.IsSuccess); - }) - .WhenAll()).Where(x => x.Succ) - .Select(x => x.Cmd)); + { + var pre = await x.CheckPreconditionsAsync(Context, _services); + return (Cmd: x, Succ: pre.IsSuccess); + }) + .WhenAll()).Where(x => x.Succ) + .Select(x => x.Cmd)); if (opts.View == CommandsOptions.ViewType.Hide) // if hidden is specified, completely remove these commands from the list @@ -232,8 +252,8 @@ public partial class Help : NadekoModule } var cmdsWithGroup = cmds.GroupBy(c => c.Module.GetGroupName()) - .OrderBy(x => x.Key == x.First().Module.Name ? int.MaxValue : x.Count()) - .ToList(); + .OrderBy(x => x.Key == x.First().Module.Name ? int.MaxValue : x.Count()) + .ToList(); if (cmdsWithGroup.Count == 0) { @@ -253,28 +273,28 @@ public partial class Help : NadekoModule for (var i = 0; i < last; i++) { var transformed = g.ElementAt(i) - .Select(x => - { - //if cross is specified, and the command doesn't satisfy the requirements, cross it out - if (opts.View == CommandsOptions.ViewType.Cross) - { - return - $"{(succ.Contains(x) ? "✅" : "❌")}{prefix + x.Aliases.First(),-15} {"[" + x.Aliases.Skip(1).FirstOrDefault() + "]",-8}"; - } + .Select(x => + { + //if cross is specified, and the command doesn't satisfy the requirements, cross it out + if (opts.View == CommandsOptions.ViewType.Cross) + { + return + $"{(succ.Contains(x) ? "✅" : "❌")}{prefix + x.Aliases.First(),-15} {"[" + x.Aliases.Skip(1).FirstOrDefault() + "]",-8}"; + } - return - $"{prefix + x.Aliases.First(),-15} {"[" + x.Aliases.Skip(1).FirstOrDefault() + "]",-8}"; - }); + return + $"{prefix + x.Aliases.First(),-15} {"[" + x.Aliases.Skip(1).FirstOrDefault() + "]",-8}"; + }); if (i == last - 1 && (i + 1) % 2 != 0) { transformed = transformed.Chunk(2) - .Select(x => - { - if (x.Count() == 1) - return $"{x.First()}"; - return string.Concat(x); - }); + .Select(x => + { + if (x.Count() == 1) + return $"{x.First()}"; + return string.Concat(x); + }); } embed.AddField(g.ElementAt(i).Key, "```css\n" + string.Join("\n", transformed) + "\n```", true); @@ -288,8 +308,8 @@ public partial class Help : NadekoModule private async Task Group(ModuleInfo group) { var eb = _eb.Create(ctx) - .WithTitle(GetText(strs.cmd_group_commands(group.Name))) - .WithOkColor(); + .WithTitle(GetText(strs.cmd_group_commands(group.Name))) + .WithOkColor(); foreach (var cmd in group.Commands) { @@ -315,9 +335,9 @@ public partial class Help : NadekoModule fail = fail.Substring(prefix.Length); var group = _cmds.Modules - .SelectMany(x => x.Submodules) - .Where(x => !string.IsNullOrWhiteSpace(x.Group)) - .FirstOrDefault(x => x.Group.Equals(fail, StringComparison.InvariantCultureIgnoreCase)); + .SelectMany(x => x.Submodules) + .Where(x => !string.IsNullOrWhiteSpace(x.Group)) + .FirstOrDefault(x => x.Group.Equals(fail, StringComparison.InvariantCultureIgnoreCase)); if (group is not null) { @@ -343,8 +363,13 @@ public partial class Help : NadekoModule if (data == default) return; await ch.SendAsync(data); - try { await ctx.OkAsync(); } - catch { } // ignore if bot can't react + try + { + await ctx.OkAsync(); + } + catch + { + } // ignore if bot can't react } catch (Exception) { @@ -354,7 +379,7 @@ public partial class Help : NadekoModule return; } - var embed = _service.GetCommandHelp(com, ctx.Guild); + var embed = _cus.GetCommandHelp(com, ctx.Guild); await channel.EmbedAsync(embed); } @@ -367,29 +392,29 @@ public partial class Help : NadekoModule // order commands by top level module name // and make a dictionary of > var cmdData = _cmds.Commands.GroupBy(x => x.Module.GetTopLevelModule().Name) - .OrderBy(x => x.Key) - .ToDictionary(x => x.Key, - x => x.DistinctBy(c => c.Aliases.First()) - .Select(com => - { - List optHelpStr = null; - - var opt = HelpService.GetNadekoOptionType(com.Attributes); - if (opt is not null) - optHelpStr = HelpService.GetCommandOptionHelpList(opt); + .OrderBy(x => x.Key) + .ToDictionary(x => x.Key, + x => x.DistinctBy(c => c.Aliases.First()) + .Select(com => + { + List optHelpStr = null; - return new CommandJsonObject - { - Aliases = com.Aliases.Select(alias => prefix + alias).ToArray(), - Description = com.RealSummary(_strings, _medusae, Culture, prefix), - Usage = com.RealRemarksArr(_strings, _medusae, Culture, prefix), - Submodule = com.Module.Name, - Module = com.Module.GetTopLevelModule().Name, - Options = optHelpStr, - Requirements = HelpService.GetCommandRequirements(com) - }; - }) - .ToList()); + var opt = CommandsUtilityService.GetNadekoOptionType(com.Attributes); + if (opt is not null) + optHelpStr = CommandsUtilityService.GetCommandOptionHelpList(opt); + + return new CommandJsonObject + { + Aliases = com.Aliases.Select(alias => prefix + alias).ToArray(), + Description = com.RealSummary(_strings, _medusae, Culture, prefix), + Usage = com.RealRemarksArr(_strings, _medusae, Culture, prefix), + Submodule = com.Module.Name, + Module = com.Module.GetTopLevelModule().Name, + Options = optHelpStr, + Requirements = CommandsUtilityService.GetCommandRequirements(com) + }; + }) + .ToList()); var readableData = JsonConvert.SerializeObject(cmdData, Formatting.Indented); var uploadData = JsonConvert.SerializeObject(cmdData, Formatting.None); @@ -512,8 +537,8 @@ public partial class Help : NadekoModule SelfhostAction)); var eb = _eb.Create(ctx) - .WithOkColor() - .WithTitle("Thank you for considering to donate to the NadekoBot project!"); + .WithOkColor() + .WithTitle("Thank you for considering to donate to the NadekoBot project!"); eb .WithDescription("NadekoBot relies on donations to keep the servers, services and APIs running.\n" diff --git a/src/Nadeko.Bot.Modules.Help/HelpService.cs b/src/Nadeko.Bot.Modules.Help/HelpService.cs index 678a0ce7b..96bc87fac 100644 --- a/src/Nadeko.Bot.Modules.Help/HelpService.cs +++ b/src/Nadeko.Bot.Modules.Help/HelpService.cs @@ -1,37 +1,15 @@ #nullable disable -using CommandLine; -using Nadeko.Common; -using Nadeko.Medusa; using NadekoBot.Common.ModuleBehaviors; namespace NadekoBot.Modules.Help.Services; public class HelpService : IExecNoCommand, INService { - private readonly CommandHandler _ch; - private readonly IBotStrings _strings; - private readonly DiscordPermOverrideService _dpos; private readonly BotConfigService _bss; - private readonly IEmbedBuilderService _eb; - private readonly ILocalization _loc; - private readonly IMedusaLoaderService _medusae; - public HelpService( - CommandHandler ch, - IBotStrings strings, - DiscordPermOverrideService dpos, - BotConfigService bss, - IEmbedBuilderService eb, - ILocalization loc, - IMedusaLoaderService medusae) + public HelpService(BotConfigService bss) { - _ch = ch; - _strings = strings; - _dpos = dpos; _bss = bss; - _eb = eb; - _loc = loc; - _medusae = medusae; } public Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg) @@ -44,13 +22,14 @@ public class HelpService : IExecNoCommand, INService // only send dm help text if it contains one of the keywords, if they're specified // if they're not, then reply to every DM - if (settings.DmHelpTextKeywords is not null && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k))) + if (settings.DmHelpTextKeywords is not null && + !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k))) return Task.CompletedTask; var rep = new ReplacementBuilder().WithOverride("%prefix%", () => _bss.Data.Prefix) - .WithOverride("%bot.prefix%", () => _bss.Data.Prefix) - .WithUser(msg.Author) - .Build(); + .WithOverride("%bot.prefix%", () => _bss.Data.Prefix) + .WithUser(msg.Author) + .Build(); var text = SmartText.CreateFrom(settings.DmHelpText); text = rep.Replace(text); @@ -60,143 +39,4 @@ public class HelpService : IExecNoCommand, INService return Task.CompletedTask; } - - public IEmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild) - { - var prefix = _ch.GetPrefix(guild); - - var str = $"**`{prefix + com.Aliases.First()}`**"; - var alias = com.Aliases.Skip(1).FirstOrDefault(); - if (alias is not null) - str += $" **/ `{prefix + alias}`**"; - - var culture = _loc.GetCultureInfo(guild); - - var em = _eb.Create() - .AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true); - - _dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides); - var reqs = GetCommandRequirements(com, (GuildPermission?)overrides); - if (reqs.Any()) - em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs)); - - em.AddField(_strings.GetText(strs.usage), - string.Join("\n", com.RealRemarksArr(_strings,_medusae, culture, prefix).Map(arg => Format.Code(arg)))) - .WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild)) - .WithOkColor(); - - var opt = GetNadekoOptionType(com.Attributes); - if (opt is not null) - { - var hs = GetCommandOptionHelp(opt); - if (!string.IsNullOrWhiteSpace(hs)) - em.AddField(GetText(strs.options, guild), hs); - } - - return em; - } - - public static Type GetNadekoOptionType(IEnumerable attributes) - => attributes - .Select(a => a.GetType()) - .Where(a => a.IsGenericType - && a.GetGenericTypeDefinition() == typeof(NadekoOptionsAttribute<>)) - .Select(a => a.GenericTypeArguments[0]) - .FirstOrDefault(); - - public static string GetCommandOptionHelp(Type opt) - { - var strs = GetCommandOptionHelpList(opt); - - return string.Join("\n", strs); - } - - public static List GetCommandOptionHelpList(Type opt) - { - var strs = opt.GetProperties() - .Select(x => x.GetCustomAttributes(true).FirstOrDefault(a => a is OptionAttribute)) - .Where(x => x is not null) - .Cast() - .Select(x => - { - var toReturn = $"`--{x.LongName}`"; - - if (!string.IsNullOrWhiteSpace(x.ShortName)) - toReturn += $" (`-{x.ShortName}`)"; - - toReturn += $" {x.HelpText} "; - return toReturn; - }) - .ToList(); - - return strs; - } - - - public static string[] GetCommandRequirements(CommandInfo cmd, GuildPerm? overrides = null) - { - var toReturn = new List(); - - if (cmd.Preconditions.Any(x => x is OwnerOnlyAttribute)) - toReturn.Add("Bot Owner Only"); - - if(cmd.Preconditions.Any(x => x is NoPublicBotAttribute) - || cmd.Module - .Preconditions - .Any(x => x is NoPublicBotAttribute) - || cmd.Module.GetTopLevelModule() - .Preconditions - .Any(x => x is NoPublicBotAttribute)) - toReturn.Add("No Public Bot"); - - if (cmd.Preconditions - .Any(x => x is OnlyPublicBotAttribute) - || cmd.Module - .Preconditions - .Any(x => x is OnlyPublicBotAttribute) - || cmd.Module.GetTopLevelModule() - .Preconditions - .Any(x => x is OnlyPublicBotAttribute)) - toReturn.Add("Only Public Bot"); - - var userPermString = cmd.Preconditions - .Where(ca => ca is UserPermAttribute) - .Cast() - .Select(userPerm => - { - if (userPerm.ChannelPermission is { } cPerm) - return GetPreconditionString(cPerm); - - if (userPerm.GuildPermission is { } gPerm) - return GetPreconditionString(gPerm); - - return string.Empty; - }) - .Where(x => !string.IsNullOrWhiteSpace(x)) - .Join('\n'); - - if (overrides is null) - { - if (!string.IsNullOrWhiteSpace(userPermString)) - toReturn.Add(userPermString); - } - else - { - if (!string.IsNullOrWhiteSpace(userPermString)) - toReturn.Add(Format.Strikethrough(userPermString)); - - toReturn.Add(GetPreconditionString(overrides.Value)); - } - - return toReturn.ToArray(); - } - - public static string GetPreconditionString(ChannelPerm perm) - => (perm + " Channel Permission").Replace("Guild", "Server"); - - public static string GetPreconditionString(GuildPerm perm) - => (perm + " Server Permission").Replace("Guild", "Server"); - - private string GetText(LocStr str, IGuild guild) - => _strings.GetText(str, guild?.Id); -} +} \ No newline at end of file diff --git a/src/Nadeko.Bot.Modules.Help/Nadeko.Bot.Modules.Help.csproj b/src/Nadeko.Bot.Modules.Help/Nadeko.Bot.Modules.Help.csproj index 88326f525..4018c363d 100644 --- a/src/Nadeko.Bot.Modules.Help/Nadeko.Bot.Modules.Help.csproj +++ b/src/Nadeko.Bot.Modules.Help/Nadeko.Bot.Modules.Help.csproj @@ -7,11 +7,13 @@ - + + + - + - + diff --git a/src/Nadeko.Bot.Modules.Music/Nadeko.Bot.Modules.Music.csproj b/src/Nadeko.Bot.Modules.Music/Nadeko.Bot.Modules.Music.csproj index 00a63d5fa..2e2dfd4c1 100644 --- a/src/Nadeko.Bot.Modules.Music/Nadeko.Bot.Modules.Music.csproj +++ b/src/Nadeko.Bot.Modules.Music/Nadeko.Bot.Modules.Music.csproj @@ -7,13 +7,15 @@ - - - + + + + + - + - + diff --git a/src/Nadeko.Bot.Modules.Patronage/GlobalUsings.cs b/src/Nadeko.Bot.Modules.Patronage/GlobalUsings.cs new file mode 100644 index 000000000..f36328bd4 --- /dev/null +++ b/src/Nadeko.Bot.Modules.Patronage/GlobalUsings.cs @@ -0,0 +1,31 @@ +// global using System.Collections.Concurrent; +global using NonBlocking; + +// packages +global using Serilog; +global using Humanizer; + +// nadekobot +global using NadekoBot; +global using NadekoBot.Services; +global using Nadeko.Common; // new project +global using NadekoBot.Common; // old + nadekobot specific things +global using NadekoBot.Common.Attributes; +global using NadekoBot.Extensions; +global using Nadeko.Snake; + +// discord +global using Discord; +global using Discord.Commands; +global using Discord.Net; +global using Discord.WebSocket; + +// aliases +global using GuildPerm = Discord.GuildPermission; +global using ChannelPerm = Discord.ChannelPermission; +global using BotPermAttribute = Discord.Commands.RequireBotPermissionAttribute; +global using LeftoverAttribute = Discord.Commands.RemainderAttribute; +global using TypeReaderResult = NadekoBot.Common.TypeReaders.TypeReaderResult; + +// non-essential +global using JetBrains.Annotations; \ No newline at end of file diff --git a/src/Nadeko.Bot.Modules.Patronage/Nadeko.Bot.Modules.Patronage.csproj b/src/Nadeko.Bot.Modules.Patronage/Nadeko.Bot.Modules.Patronage.csproj new file mode 100644 index 000000000..49ff4f201 --- /dev/null +++ b/src/Nadeko.Bot.Modules.Patronage/Nadeko.Bot.Modules.Patronage.csproj @@ -0,0 +1,16 @@ + + + + net7.0 + enable + enable + + + + + + + + + + diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/Config/PatronageConfig.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/Config/PatronageConfig.cs similarity index 87% rename from src/Nadeko.Bot.Modules.Utility/Patronage/Config/PatronageConfig.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/Config/PatronageConfig.cs index f5bef4a91..e4c9339b4 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/Config/PatronageConfig.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/Config/PatronageConfig.cs @@ -1,6 +1,6 @@ using NadekoBot.Common.Configs; -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public class PatronageConfig : ConfigServiceBase { @@ -8,7 +8,7 @@ public class PatronageConfig : ConfigServiceBase => "patron"; private static readonly TypedKey _changeKey - = new TypedKey("config.patron.updated"); + = new("config.patron.updated"); private const string FILE_PATH = "data/patron.yml"; diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/CurrencyRewardService.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/CurrencyRewardService.cs similarity index 96% rename from src/Nadeko.Bot.Modules.Utility/Patronage/CurrencyRewardService.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/CurrencyRewardService.cs index 800471529..4c4812c38 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/CurrencyRewardService.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/CurrencyRewardService.cs @@ -1,20 +1,18 @@ #nullable disable using LinqToDB; using LinqToDB.EntityFrameworkCore; -using NadekoBot.Modules.Utility.Patronage; -using NadekoBot.Modules.Gambling.Bank; using NadekoBot.Modules.Gambling.Services; +using NadekoBot.Modules.Patronage; using NadekoBot.Services.Currency; using NadekoBot.Services.Database.Models; namespace NadekoBot.Modules.Utility; -public class CurrencyRewardService : INService, IDisposable +public sealed class CurrencyRewardService : INService, IDisposable { private readonly ICurrencyService _cs; private readonly IPatronageService _ps; private readonly DbService _db; - private readonly IBankService _bs; private readonly IEmbedBuilderService _eb; private readonly GamblingConfigService _config; private readonly DiscordSocketClient _client; @@ -23,7 +21,6 @@ public class CurrencyRewardService : INService, IDisposable ICurrencyService cs, IPatronageService ps, DbService db, - IBankService bs, IEmbedBuilderService eb, GamblingConfigService config, DiscordSocketClient client) @@ -31,7 +28,6 @@ public class CurrencyRewardService : INService, IDisposable _cs = cs; _ps = ps; _db = db; - _bs = bs; _eb = eb; _config = config; _client = client; diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/InsufficientTier.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/InsufficientTier.cs similarity index 84% rename from src/Nadeko.Bot.Modules.Utility/Patronage/InsufficientTier.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/InsufficientTier.cs index 7d478b98d..3bd213498 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/InsufficientTier.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/InsufficientTier.cs @@ -1,6 +1,6 @@ using NadekoBot.Db.Models; -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; public readonly struct InsufficientTier { diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonClient.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonClient.cs similarity index 98% rename from src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonClient.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonClient.cs index 63d1184e3..b210ac11c 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonClient.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonClient.cs @@ -1,11 +1,10 @@ #nullable disable -using NadekoBot.Modules.Utility.Common.Patreon; using OneOf; using OneOf.Types; using System.Net.Http.Json; using System.Text.Json; -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; public class PatreonClient : IDisposable { diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonCredentials.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonCredentials.cs similarity index 86% rename from src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonCredentials.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonCredentials.cs index 67f619a30..db3c205ee 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonCredentials.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonCredentials.cs @@ -1,5 +1,5 @@ #nullable disable -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; public readonly struct PatreonCredentials { diff --git a/src/Nadeko.Bot.Modules.Utility/_Common/Patreon/PatreonData.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonData.cs similarity index 98% rename from src/Nadeko.Bot.Modules.Utility/_Common/Patreon/PatreonData.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonData.cs index 9a855e004..713ea030b 100644 --- a/src/Nadeko.Bot.Modules.Utility/_Common/Patreon/PatreonData.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonData.cs @@ -1,7 +1,7 @@ #nullable disable using System.Text.Json.Serialization; -namespace NadekoBot.Modules.Utility.Common.Patreon; +namespace NadekoBot.Modules.Patronage; public sealed class Attributes { diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonMemberData.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonMemberData.cs similarity index 96% rename from src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonMemberData.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonMemberData.cs index f4c309348..6a5035f2a 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonMemberData.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonMemberData.cs @@ -1,5 +1,5 @@ #nullable disable -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; public sealed class PatreonMemberData : ISubscriberData { diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonRefreshData.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonRefreshData.cs similarity index 92% rename from src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonRefreshData.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonRefreshData.cs index 917c450cd..f761b8348 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonRefreshData.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonRefreshData.cs @@ -1,7 +1,7 @@ #nullable disable using System.Text.Json.Serialization; -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; public sealed class PatreonRefreshData { diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonSubscriptionHandler.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonSubscriptionHandler.cs similarity index 93% rename from src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonSubscriptionHandler.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonSubscriptionHandler.cs index 4c17e2ae9..b465b7e49 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/Patreon/PatreonSubscriptionHandler.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/Patreon/PatreonSubscriptionHandler.cs @@ -1,15 +1,13 @@ #nullable disable -using NadekoBot.Modules.Utility.Patronage; - -namespace NadekoBot.Modules.Utility; +namespace NadekoBot.Modules.Patronage; /// /// Service tasked with handling pledges on patreon /// public sealed class PatreonSubscriptionHandler : ISubscriptionHandler, INService { - private readonly IBotCredsProvider _credsProvider; - private readonly PatreonClient _patreonClient; + private readonly IBotCredsProvider _credsProvider; + private readonly PatreonClient _patreonClient; public PatreonSubscriptionHandler(IBotCredsProvider credsProvider) { diff --git a/src/Nadeko.Bot.Modules.Patronage/Patronage/PatronageCommands.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/PatronageCommands.cs new file mode 100644 index 000000000..9fd961929 --- /dev/null +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/PatronageCommands.cs @@ -0,0 +1,148 @@ +namespace NadekoBot.Modules.Patronage; + +[OnlyPublicBot] +public partial class Patronage : NadekoModule +{ + private readonly PatronageService _service; + private readonly PatronageConfig _pConf; + + public Patronage(PatronageService service, PatronageConfig pConf) + { + _service = service; + _pConf = pConf; + } + + [Cmd] + [Priority(2)] + public Task Patron() + => InternalPatron(ctx.User); + + [Cmd] + [Priority(0)] + [OwnerOnly] + public Task Patron(IUser user) + => InternalPatron(user); + + [Cmd] + [Priority(0)] + [OwnerOnly] + public async Task PatronMessage(PatronTier tierAndHigher, string message) + { + _ = ctx.Channel.TriggerTypingAsync(); + var result = await _service.SendMessageToPatronsAsync(tierAndHigher, message); + + await ReplyConfirmLocalizedAsync(strs.patron_msg_sent( + Format.Code(tierAndHigher.ToString()), + Format.Bold(result.Success.ToString()), + Format.Bold(result.Failed.ToString()))); + } + + // [Cmd] + // [OwnerOnly] + // public async Task PatronGift(IUser user, int amount) + // { + // // i can't figure out a good way to gift more than one month at the moment. + // + // if (amount < 1) + // return; + // + // var patron = _service.GiftPatronAsync(user, amount); + // + // var eb = _eb.Create(ctx); + // + // await ctx.Channel.EmbedAsync(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!") + // .AddField("Tier", Format.Bold(patron.Tier.ToString()), true) + // .AddField("Amount", $"**{patron.Amount / 100.0f:N1}$**", true) + // .AddField("Until", TimestampTag.FromDateTime(patron.ValidThru.AddDays(1)))); + // + // + // } + + private async Task InternalPatron(IUser user) + { + if (!_pConf.Data.IsEnabled) + { + await ReplyErrorLocalizedAsync(strs.patron_not_enabled); + return; + } + + var patron = await _service.GetPatronAsync(user.Id); + var quotaStats = await _service.GetUserQuotaStatistic(user.Id); + + var eb = _eb.Create(ctx) + .WithAuthor(user) + .WithTitle(GetText(strs.patron_info)) + .WithOkColor(); + + if (quotaStats.Commands.Count == 0 + && quotaStats.Groups.Count == 0 + && quotaStats.Modules.Count == 0) + { + eb.WithDescription(GetText(strs.no_quota_found)); + } + else + { + eb.AddField(GetText(strs.tier), Format.Bold(patron.Tier.ToFullName()), true) + .AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true); + + if (patron.Tier != PatronTier.None) + eb.AddField(GetText(strs.expires), patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(), true); + + eb.AddField(GetText(strs.quotas), "⁣", false); + + if (quotaStats.Commands.Count > 0) + { + var text = GetQuotaList(quotaStats.Commands); + if (!string.IsNullOrWhiteSpace(text)) + eb.AddField(GetText(strs.commands), text, true); + } + + if (quotaStats.Groups.Count > 0) + { + var text = GetQuotaList(quotaStats.Groups); + if (!string.IsNullOrWhiteSpace(text)) + eb.AddField(GetText(strs.groups), text, true); + } + + if (quotaStats.Modules.Count > 0) + { + var text = GetQuotaList(quotaStats.Modules); + if (!string.IsNullOrWhiteSpace(text)) + eb.AddField(GetText(strs.modules), text, true); + } + } + + + try + { + await ctx.User.EmbedAsync(eb); + _ = ctx.OkAsync(); + } + catch + { + await ReplyErrorLocalizedAsync(strs.cant_dm); + } + } + + private string GetQuotaList(IReadOnlyDictionary featureQuotaStats) + { + var text = string.Empty; + foreach (var (key, q) in featureQuotaStats) + { + text += $"\n⁣\t`{key}`\n"; + if (q.Hourly != default) + text += $"⁣ ⁣ {GetEmoji(q.Hourly)} {q.Hourly.Cur}/{q.Hourly.Max} per hour\n"; + if (q.Daily != default) + text += $"⁣ ⁣ {GetEmoji(q.Daily)} {q.Daily.Cur}/{q.Daily.Max} per day\n"; + if (q.Monthly != default) + text += $"⁣ ⁣ {GetEmoji(q.Monthly)} {q.Monthly.Cur}/{q.Monthly.Max} per month\n"; + } + + return text; + } + + private string GetEmoji((uint Cur, uint Max) limit) + => limit.Cur < limit.Max + ? "✅" + : "⚠️"; +} \ No newline at end of file diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/PatronageService.cs b/src/Nadeko.Bot.Modules.Patronage/Patronage/PatronageService.cs similarity index 99% rename from src/Nadeko.Bot.Modules.Utility/Patronage/PatronageService.cs rename to src/Nadeko.Bot.Modules.Patronage/Patronage/PatronageService.cs index 761ec55bc..49b3071d9 100644 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/PatronageService.cs +++ b/src/Nadeko.Bot.Modules.Patronage/Patronage/PatronageService.cs @@ -6,7 +6,7 @@ using OneOf; using OneOf.Types; using CommandInfo = Discord.Commands.CommandInfo; -namespace NadekoBot.Modules.Utility.Patronage; +namespace NadekoBot.Modules.Patronage; /// public sealed class PatronageService diff --git a/src/Nadeko.Bot.Modules.Permisssions/Nadeko.Bot.Modules.Permisssions.csproj b/src/Nadeko.Bot.Modules.Permisssions/Nadeko.Bot.Modules.Permisssions.csproj index 3a88c91f3..1cd65638c 100644 --- a/src/Nadeko.Bot.Modules.Permisssions/Nadeko.Bot.Modules.Permisssions.csproj +++ b/src/Nadeko.Bot.Modules.Permisssions/Nadeko.Bot.Modules.Permisssions.csproj @@ -7,12 +7,14 @@ - - + + + + - + - + diff --git a/src/Nadeko.Bot.Modules.Searches/Nadeko.Bot.Modules.Searches.csproj b/src/Nadeko.Bot.Modules.Searches/Nadeko.Bot.Modules.Searches.csproj index 99c3e8660..1f61198f4 100644 --- a/src/Nadeko.Bot.Modules.Searches/Nadeko.Bot.Modules.Searches.csproj +++ b/src/Nadeko.Bot.Modules.Searches/Nadeko.Bot.Modules.Searches.csproj @@ -7,25 +7,27 @@ - - + + + + - - - - - - + + + + + + - + - - + + - + diff --git a/src/Nadeko.Bot.Modules.Utility/Info/InfoCommands.cs b/src/Nadeko.Bot.Modules.Utility/Info/InfoCommands.cs index 40c7d1947..5f15947f7 100644 --- a/src/Nadeko.Bot.Modules.Utility/Info/InfoCommands.cs +++ b/src/Nadeko.Bot.Modules.Utility/Info/InfoCommands.cs @@ -1,7 +1,6 @@ #nullable disable -using NadekoBot.Modules.Utility.Patronage; using System.Text; -using Nadeko.Common; +using NadekoBot.Modules.Patronage; namespace NadekoBot.Modules.Utility; diff --git a/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj b/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj index 6a30e8002..807f6287f 100644 --- a/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj +++ b/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj @@ -5,21 +5,23 @@ enable enable - + - - + + - - + + - - + + - + - - + + + + diff --git a/src/Nadeko.Bot.Modules.Utility/Patronage/PatronageCommands.cs b/src/Nadeko.Bot.Modules.Utility/Patronage/PatronageCommands.cs deleted file mode 100644 index d598d4dfd..000000000 --- a/src/Nadeko.Bot.Modules.Utility/Patronage/PatronageCommands.cs +++ /dev/null @@ -1,153 +0,0 @@ -using NadekoBot.Modules.Utility.Patronage; - -namespace NadekoBot.Modules.Utility; - -public partial class Utility -{ - [OnlyPublicBot] - public partial class PatronageCommands : NadekoModule - { - private readonly PatronageService _service; - private readonly PatronageConfig _pConf; - - public PatronageCommands(PatronageService service, PatronageConfig pConf) - { - _service = service; - _pConf = pConf; - } - - [Cmd] - [Priority(2)] - public Task Patron() - => InternalPatron(ctx.User); - - [Cmd] - [Priority(0)] - [OwnerOnly] - public Task Patron(IUser user) - => InternalPatron(user); - - [Cmd] - [Priority(0)] - [OwnerOnly] - public async Task PatronMessage(PatronTier tierAndHigher, string message) - { - _ = ctx.Channel.TriggerTypingAsync(); - var result = await _service.SendMessageToPatronsAsync(tierAndHigher, message); - - await ReplyConfirmLocalizedAsync(strs.patron_msg_sent( - Format.Code(tierAndHigher.ToString()), - Format.Bold(result.Success.ToString()), - Format.Bold(result.Failed.ToString()))); - } - - // [Cmd] - // [OwnerOnly] - // public async Task PatronGift(IUser user, int amount) - // { - // // i can't figure out a good way to gift more than one month at the moment. - // - // if (amount < 1) - // return; - // - // var patron = _service.GiftPatronAsync(user, amount); - // - // var eb = _eb.Create(ctx); - // - // await ctx.Channel.EmbedAsync(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!") - // .AddField("Tier", Format.Bold(patron.Tier.ToString()), true) - // .AddField("Amount", $"**{patron.Amount / 100.0f:N1}$**", true) - // .AddField("Until", TimestampTag.FromDateTime(patron.ValidThru.AddDays(1)))); - // - // - // } - - private async Task InternalPatron(IUser user) - { - if (!_pConf.Data.IsEnabled) - { - await ReplyErrorLocalizedAsync(strs.patron_not_enabled); - return; - } - - var patron = await _service.GetPatronAsync(user.Id); - var quotaStats = await _service.GetUserQuotaStatistic(user.Id); - - var eb = _eb.Create(ctx) - .WithAuthor(user) - .WithTitle(GetText(strs.patron_info)) - .WithOkColor(); - - if (quotaStats.Commands.Count == 0 - && quotaStats.Groups.Count == 0 - && quotaStats.Modules.Count == 0) - { - eb.WithDescription(GetText(strs.no_quota_found)); - } - else - { - eb.AddField(GetText(strs.tier), Format.Bold(patron.Tier.ToFullName()), true) - .AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true); - - if (patron.Tier != PatronTier.None) - eb.AddField(GetText(strs.expires), patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(), true); - - eb.AddField(GetText(strs.quotas), "⁣", false); - - if (quotaStats.Commands.Count > 0) - { - var text = GetQuotaList(quotaStats.Commands); - if (!string.IsNullOrWhiteSpace(text)) - eb.AddField(GetText(strs.commands), text, true); - } - - if (quotaStats.Groups.Count > 0) - { - var text = GetQuotaList(quotaStats.Groups); - if (!string.IsNullOrWhiteSpace(text)) - eb.AddField(GetText(strs.groups), text, true); - } - - if (quotaStats.Modules.Count > 0) - { - var text = GetQuotaList(quotaStats.Modules); - if (!string.IsNullOrWhiteSpace(text)) - eb.AddField(GetText(strs.modules), text, true); - } - } - - - try - { - await ctx.User.EmbedAsync(eb); - _ = ctx.OkAsync(); - } - catch - { - await ReplyErrorLocalizedAsync(strs.cant_dm); - } - } - - private string GetQuotaList(IReadOnlyDictionary featureQuotaStats) - { - var text = string.Empty; - foreach (var (key, q) in featureQuotaStats) - { - text += $"\n⁣\t`{key}`\n"; - if (q.Hourly != default) - text += $"⁣ ⁣ {GetEmoji(q.Hourly)} {q.Hourly.Cur}/{q.Hourly.Max} per hour\n"; - if (q.Daily != default) - text += $"⁣ ⁣ {GetEmoji(q.Daily)} {q.Daily.Cur}/{q.Daily.Max} per day\n"; - if (q.Monthly != default) - text += $"⁣ ⁣ {GetEmoji(q.Monthly)} {q.Monthly.Cur}/{q.Monthly.Max} per month\n"; - } - - return text; - } - - private string GetEmoji((uint Cur, uint Max) limit) - => limit.Cur < limit.Max - ? "✅" - : "⚠️"; - } -} \ No newline at end of file diff --git a/src/Nadeko.Bot.Modules.Utility/VerboseErrorsService.cs b/src/Nadeko.Bot.Modules.Utility/VerboseErrorsService.cs index 2829995a9..1dc393cf6 100644 --- a/src/Nadeko.Bot.Modules.Utility/VerboseErrorsService.cs +++ b/src/Nadeko.Bot.Modules.Utility/VerboseErrorsService.cs @@ -1,6 +1,5 @@ #nullable disable using NadekoBot.Db; -using NadekoBot.Modules.Help.Services; namespace NadekoBot.Modules.Utility.Services; @@ -9,13 +8,13 @@ public class VerboseErrorsService : INService private readonly ConcurrentHashSet _guildsDisabled; private readonly DbService _db; private readonly CommandHandler _ch; - private readonly HelpService _hs; + private readonly ICommandsUtilityService _hs; public VerboseErrorsService( IBot bot, DbService db, CommandHandler ch, - HelpService hs) + ICommandsUtilityService hs) { _db = db; _ch = ch; diff --git a/src/Nadeko.Bot.Modules.Xp/Nadeko.Bot.Modules.Xp.csproj b/src/Nadeko.Bot.Modules.Xp/Nadeko.Bot.Modules.Xp.csproj index 1233b9592..dc514acc1 100644 --- a/src/Nadeko.Bot.Modules.Xp/Nadeko.Bot.Modules.Xp.csproj +++ b/src/Nadeko.Bot.Modules.Xp/Nadeko.Bot.Modules.Xp.csproj @@ -7,12 +7,15 @@ - - + + - - + + + + + diff --git a/src/Nadeko.Bot.Modules.Xp/Xp.cs b/src/Nadeko.Bot.Modules.Xp/Xp.cs index fd861a339..be3e86c34 100644 --- a/src/Nadeko.Bot.Modules.Xp/Xp.cs +++ b/src/Nadeko.Bot.Modules.Xp/Xp.cs @@ -1,11 +1,10 @@ #nullable disable warnings using NadekoBot.Modules.Xp.Services; using NadekoBot.Services.Database.Models; -using System.Globalization; using Nadeko.Bot.Common; using NadekoBot.Db; using NadekoBot.Db.Models; -using NadekoBot.Modules.Utility.Patronage; +using NadekoBot.Modules.Patronage; namespace NadekoBot.Modules.Xp; diff --git a/src/Nadeko.Bot.Modules.Xp/XpConfig.cs b/src/Nadeko.Bot.Modules.Xp/XpConfig.cs index 54c0a78d3..d6e4e9d6d 100644 --- a/src/Nadeko.Bot.Modules.Xp/XpConfig.cs +++ b/src/Nadeko.Bot.Modules.Xp/XpConfig.cs @@ -2,7 +2,7 @@ using Cloneable; using NadekoBot.Common.Yml; using NadekoBot.Db.Models; -using NadekoBot.Modules.Utility.Patronage; +using NadekoBot.Modules.Patronage; namespace NadekoBot.Modules.Xp; diff --git a/src/Nadeko.Bot.Modules.Xp/XpService.cs b/src/Nadeko.Bot.Modules.Xp/XpService.cs index 9fca4e642..6ebbd766b 100644 --- a/src/Nadeko.Bot.Modules.Xp/XpService.cs +++ b/src/Nadeko.Bot.Modules.Xp/XpService.cs @@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Db; using NadekoBot.Db.Models; -using NadekoBot.Modules.Utility.Patronage; using NadekoBot.Services.Database.Models; using Newtonsoft.Json; using SixLabors.Fonts; @@ -15,7 +14,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using System.Threading.Channels; using LinqToDB.EntityFrameworkCore; -using NadekoBot.Services; +using NadekoBot.Modules.Patronage; using Color = SixLabors.ImageSharp.Color; using Exception = System.Exception; using Image = SixLabors.ImageSharp.Image; diff --git a/src/Nadeko.Common/Nadeko.Common.csproj b/src/Nadeko.Common/Nadeko.Common.csproj index d6c83a15f..9883c5a0b 100644 --- a/src/Nadeko.Common/Nadeko.Common.csproj +++ b/src/Nadeko.Common/Nadeko.Common.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Nadeko.Medusa/Extensions/EmbedBuilderExtensions.cs b/src/Nadeko.Medusa/Extensions/EmbedBuilderExtensions.cs index 6506308c4..f6ff9f235 100644 --- a/src/Nadeko.Medusa/Extensions/EmbedBuilderExtensions.cs +++ b/src/Nadeko.Medusa/Extensions/EmbedBuilderExtensions.cs @@ -10,5 +10,4 @@ public static class EmbedBuilderExtensions public static IEmbedBuilder WithErrorColor(this IEmbedBuilder eb) => eb.WithColor(EmbedColor.Error); - } \ No newline at end of file diff --git a/src/Nadeko.Medusa/Nadeko.Medusa.csproj b/src/Nadeko.Medusa/Nadeko.Medusa.csproj index 8adb79a0f..761571fc5 100644 --- a/src/Nadeko.Medusa/Nadeko.Medusa.csproj +++ b/src/Nadeko.Medusa/Nadeko.Medusa.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/NadekoBot.Coordinator/NadekoBot.Coordinator.csproj b/src/NadekoBot.Coordinator/NadekoBot.Coordinator.csproj index f5535c759..b9b8dcf8f 100644 --- a/src/NadekoBot.Coordinator/NadekoBot.Coordinator.csproj +++ b/src/NadekoBot.Coordinator/NadekoBot.Coordinator.csproj @@ -10,11 +10,11 @@ - + - + diff --git a/src/NadekoBot.Tests/NadekoBot.Tests.csproj b/src/NadekoBot.Tests/NadekoBot.Tests.csproj index 4f0b4b54d..eadf0eb94 100644 --- a/src/NadekoBot.Tests/NadekoBot.Tests.csproj +++ b/src/NadekoBot.Tests/NadekoBot.Tests.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/src/NadekoBot/Bot.cs b/src/NadekoBot/Bot.cs index 21b6072ab..251d85bee 100644 --- a/src/NadekoBot/Bot.cs +++ b/src/NadekoBot/Bot.cs @@ -4,8 +4,16 @@ using Microsoft.Extensions.DependencyInjection; using NadekoBot.Common.Configs; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Db; +using NadekoBot.Modules.Administration; +using NadekoBot.Modules.Gambling; +using NadekoBot.Modules.Help; +using NadekoBot.Modules.Music; using NadekoBot.Modules.NadekoExpressions; +using NadekoBot.Modules.Patronage; +using NadekoBot.Modules.Permissions; +using NadekoBot.Modules.Searches; using NadekoBot.Modules.Utility; +using NadekoBot.Modules.Xp; using NadekoBot.Services.Database.Models; using Ninject; using Ninject.Planning; @@ -94,6 +102,15 @@ public sealed class Bot : IBot // modules typeof(NadekoExpressions).Assembly, + typeof(Administration).Assembly, + typeof(Gambling).Assembly, + typeof(Help).Assembly, + typeof(Music).Assembly, + typeof(Patronage).Assembly, + typeof(Permissions).Assembly, + typeof(Searches).Assembly, + typeof(Utility).Assembly, + typeof(Xp).Assembly, }; } @@ -161,7 +178,6 @@ public sealed class Bot : IBot //initialize Services Services = kernel; Services.GetRequiredService().Initialize(); - Services.GetRequiredService(); if (Client.ShardId == 0) ApplyConfigMigrations(); diff --git a/src/NadekoBot/Medusa/MedusaAssemblyLoadContext.cs b/src/NadekoBot/Medusa/MedusaAssemblyLoadContext.cs index a6930f04e..2d39fa797 100644 --- a/src/NadekoBot/Medusa/MedusaAssemblyLoadContext.cs +++ b/src/NadekoBot/Medusa/MedusaAssemblyLoadContext.cs @@ -17,7 +17,7 @@ public class MedusaAssemblyLoadContext : AssemblyLoadContext var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); if (assemblyPath != null) { - Assembly assembly = LoadFromAssemblyPath(assemblyPath); + var assembly = LoadFromAssemblyPath(assemblyPath); LoadDependencies(assembly); return assembly; } diff --git a/src/NadekoBot/Medusa/MedusaIoCKernelModule.cs b/src/NadekoBot/Medusa/MedusaIoCKernelModule.cs index d9de7e786..e6f3f513e 100644 --- a/src/NadekoBot/Medusa/MedusaIoCKernelModule.cs +++ b/src/NadekoBot/Medusa/MedusaIoCKernelModule.cs @@ -6,10 +6,12 @@ using Ninject.Modules; using Ninject.Planning; using System.Text.Json; +namespace Nadeko.Medusa; + public sealed class MedusaNinjectModule : NinjectModule { public override string Name { get; } - private volatile bool _isLoaded = false; + private volatile bool isLoaded = false; private readonly Dictionary _types; public MedusaNinjectModule(Assembly assembly, string name) @@ -24,7 +26,7 @@ public sealed class MedusaNinjectModule : NinjectModule public override void Load() { - if (_isLoaded) + if (isLoaded) return; foreach (var (type, data) in _types) @@ -44,7 +46,7 @@ public sealed class MedusaNinjectModule : NinjectModule } } - _isLoaded = true; + isLoaded = true; } private Func GetScope(Lifetime lt) @@ -52,14 +54,15 @@ public sealed class MedusaNinjectModule : NinjectModule { Lifetime.Singleton => this, Lifetime.Transient => null, + _ => null, }; public override void Unload() { - if (!_isLoaded) + if (!isLoaded) return; - var planner = (RemovablePlanner)Kernel.Components.Get(); + var planner = (RemovablePlanner)Kernel!.Components.Get(); var cache = Kernel.Components.Get(); foreach (var binding in this.Bindings) { @@ -90,6 +93,6 @@ public sealed class MedusaNinjectModule : NinjectModule var clearCacheMethod = updateHandlerType?.GetMethod("ClearCache", BindingFlags.Static | BindingFlags.Public); clearCacheMethod?.Invoke(null, new object?[] { null }); - _isLoaded = false; + isLoaded = false; } } \ No newline at end of file diff --git a/src/NadekoBot/Medusa/MedusaLoaderService.cs b/src/NadekoBot/Medusa/MedusaLoaderService.cs index 85aad392b..f2d5a8a63 100644 --- a/src/NadekoBot/Medusa/MedusaLoaderService.cs +++ b/src/NadekoBot/Medusa/MedusaLoaderService.cs @@ -16,7 +16,6 @@ namespace Nadeko.Medusa; public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor, INService { private readonly CommandService _cmdService; - private readonly IServiceProvider _botServices; private readonly IBehaviorHandler _behHandler; private readonly IPubSub _pubSub; private readonly IMedusaConfigService _medusaConfig; diff --git a/src/NadekoBot/Medusa/Models/ResolvedMedusa.cs b/src/NadekoBot/Medusa/Models/ResolvedMedusa.cs index a8f98c19d..0c3e3dd3a 100644 --- a/src/NadekoBot/Medusa/Models/ResolvedMedusa.cs +++ b/src/NadekoBot/Medusa/Models/ResolvedMedusa.cs @@ -12,5 +12,5 @@ public sealed record ResolvedMedusa( IReadOnlyCollection Execs ) { - public INinjectModule KernelModule { get; set; } + public required INinjectModule KernelModule { get; set; } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Gambling/CurrencyProvider.cs b/src/NadekoBot/Modules/Gambling/CurrencyProvider.cs index 6b228a295..cff259571 100644 --- a/src/NadekoBot/Modules/Gambling/CurrencyProvider.cs +++ b/src/NadekoBot/Modules/Gambling/CurrencyProvider.cs @@ -4,7 +4,7 @@ using NadekoBot.Modules.Gambling.Services; namespace NadekoBot.Modules.Gambling; // todo do we need both currencyprovider and currencyservice -public sealed class CurrencyProvider : ICurrencyProvider +public sealed class CurrencyProvider : ICurrencyProvider, INService { private readonly GamblingConfigService _cs; diff --git a/src/NadekoBot/Modules/Medusae/IMedusaeRepositoryService.cs b/src/NadekoBot/Modules/Medusae/IMedusaeRepositoryService.cs index 769491181..4896a891b 100644 --- a/src/NadekoBot/Modules/Medusae/IMedusaeRepositoryService.cs +++ b/src/NadekoBot/Modules/Medusae/IMedusaeRepositoryService.cs @@ -1,4 +1,6 @@ -namespace NadekoBot.Modules; +using NadekoBot.Modules; + +namespace NadekoBot.Modules; public interface IMedusaeRepositoryService { diff --git a/src/NadekoBot/Modules/Medusae/MedusaItem.cs b/src/NadekoBot/Modules/Medusae/MedusaItem.cs index beffb6cfb..fae4e8076 100644 --- a/src/NadekoBot/Modules/Medusae/MedusaItem.cs +++ b/src/NadekoBot/Modules/Medusae/MedusaItem.cs @@ -1,6 +1,8 @@ -public class ModuleItem +namespace NadekoBot.Modules; + +public sealed class ModuleItem { - public string Name { get; init; } - public string Description { get; init; } - public string Command { get; init; } + public required string Name { get; init; } + public required string Description { get; init; } + public required string Command { get; init; } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Medusae/ModuleItem.cs b/src/NadekoBot/Modules/Medusae/ModuleItem.cs deleted file mode 100644 index 0aac10051..000000000 --- a/src/NadekoBot/Modules/Medusae/ModuleItem.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace NadekoBot.Modules; - -public class ModuleItem -{ - public string Name { get; init; } - public string Description { get; init; } - public string Command { get; init; } -} \ No newline at end of file diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index f59eeb8b1..1207377f8 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -24,94 +24,97 @@ all True - - - - - - - - - - + + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + all True - - - + + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - + + + + + + + + - - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -128,7 +131,7 @@ - + diff --git a/src/NadekoBot/PermissionChecker.cs b/src/NadekoBot/PermissionChecker.cs index f5498d542..9e3612222 100644 --- a/src/NadekoBot/PermissionChecker.cs +++ b/src/NadekoBot/PermissionChecker.cs @@ -24,10 +24,12 @@ public sealed class PermissionChecker : IPermissionChecker, INService IMessageChannel channel, IUser author, string module, - string cmd) + string? cmd) { + module = module.ToLowerInvariant(); + cmd = cmd?.ToLowerInvariant(); // todo add proper string - if (await _cmdCds.TryBlock(guild, author, cmd)) + if (cmd is not null && await _cmdCds.TryBlock(guild, author, cmd)) return new Error(new()); try diff --git a/src/NadekoBot/data/strings/responses/responses.en-US.json b/src/NadekoBot/data/strings/responses/responses.en-US.json index 0e176b734..452b0fe0b 100644 --- a/src/NadekoBot/data/strings/responses/responses.en-US.json +++ b/src/NadekoBot/data/strings/responses/responses.en-US.json @@ -989,6 +989,7 @@ "module_description_searches": "Search for jokes, images of animals, anime and manga", "module_description_xp": "Gain xp based on chat activity, check users' xp cards", "module_description_medusa": "**Bot Owner only.** Load, unload and handle dynamic modules. Read more [here](https://nadekobot.readthedocs.io/en/latest/medusa/creating-a-medusa/)", + "module_description_patronage": "Commands related to supporting the bot", "module_description_missing": "Description is missing for this module.", "purge_user_confirm": "Are you sure that you want to purge {0} from the database?", "expr_import_no_input": "Invalid input. No valid file upload or input text found.", diff --git a/src/ayu/Ayu.Discord.Voice/Ayu.Discord.Voice.csproj b/src/ayu/Ayu.Discord.Voice/Ayu.Discord.Voice.csproj index b46dcbdcb..c2b80e137 100644 --- a/src/ayu/Ayu.Discord.Voice/Ayu.Discord.Voice.csproj +++ b/src/ayu/Ayu.Discord.Voice/Ayu.Discord.Voice.csproj @@ -7,7 +7,7 @@ 1.0.2 - +