From 4eca5be1d43088bc40da2182dacf3f25e45bb4be Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sat, 15 Jul 2017 05:04:16 +0200 Subject: [PATCH] Huge cleanup, rewrite of the NadekoBot.cs, way services are loaded has changed. Updated discord.net. --- .../TypeReaders/BotCommandTypeReader.cs | 26 +- .../TypeReaders/GuildDateTimeTypeReader.cs | 11 +- .../Administration/Commands/MuteCommands.cs | 8 +- .../Administration/Commands/PrefixCommands.cs | 4 +- .../Administration/Commands/PruneCommands.cs | 4 +- .../Commands/RatelimitCommand.cs | 4 +- .../Administration/Commands/SelfCommands.cs | 4 +- .../Commands/UserPunishCommands.cs | 8 +- .../Modules/ClashOfClans/ClashOfClans.cs | 239 ---------------- .../CustomReactions/CustomReactions.cs | 4 +- .../Gambling/Commands/DiceRollCommand.cs | 8 +- .../Gambling/Commands/WaifuClaimCommands.cs | 4 +- src/NadekoBot/Modules/Gambling/Gambling.cs | 8 +- src/NadekoBot/Modules/Help/Help.cs | 4 +- src/NadekoBot/Modules/Music/Music.cs | 39 +-- src/NadekoBot/Modules/NSFW/NSFW.cs | 14 +- .../Searches/Commands/AnimeSearchCommands.cs | 4 +- .../Modules/Searches/Commands/XkcdCommands.cs | 4 +- .../Modules/Utility/Commands/Remind.cs | 4 +- .../Utility/Commands/RepeatCommands.cs | 5 +- src/NadekoBot/NadekoBot.cs | 210 +++----------- src/NadekoBot/NadekoBot.csproj | 2 +- .../Administration/AdministrationService.cs | 2 +- .../Administration/AutoAssignRoleService.cs | 2 +- .../Administration/GameVoiceChannelService.cs | 2 +- .../Administration/GuildTimezoneService.cs | 2 +- .../Administration/LogCommandService.cs | 2 +- .../Services/Administration/MuteService.cs | 2 +- .../Administration/PlayingRotateService.cs | 2 +- .../Administration/ProtectionService.cs | 2 +- .../Services/Administration/PruneService.cs | 2 +- .../Administration/RatelimitService.cs | 2 +- .../Services/Administration/SelfService.cs | 8 +- .../Services/Administration/VcRoleService.cs | 2 +- .../Services/Administration/VplusTService.cs | 2 +- .../ClashOfClans/ClashOfClansService.cs | 262 ------------------ .../Services/ClashOfClans/Extensions.cs | 40 --- src/NadekoBot/Services/CommandHandler.cs | 182 +++++++----- src/NadekoBot/Services/CurrencyService.cs | 2 +- .../CustomReactions/CustomReactionsService.cs | 2 +- .../Services/Database/Models/BotConfig.cs | 2 +- .../Repositories/IReminderRepository.cs | 2 +- .../Repositories/Impl/ReminderRepository.cs | 2 +- .../Services/Games/ChatterbotService.cs | 2 +- src/NadekoBot/Services/Games/GamesService.cs | 2 +- src/NadekoBot/Services/Games/PollService.cs | 2 +- .../Services/GreetSettingsService.cs | 2 +- src/NadekoBot/Services/Help/HelpService.cs | 2 +- src/NadekoBot/Services/IGoogleApiService.cs | 2 +- src/NadekoBot/Services/IImagesService.cs | 2 +- src/NadekoBot/Services/INService.cs | 16 ++ src/NadekoBot/Services/IStatsService.cs | 2 +- src/NadekoBot/Services/Impl/NadekoStrings.cs | 2 +- .../Services/Impl/SoundCloudApiService.cs | 2 +- .../Impl/StartingGuildsListService.cs | 24 ++ src/NadekoBot/Services/Music/MusicService.cs | 2 +- .../Services/Permissions/BlacklistService.cs | 2 +- .../Services/Permissions/CmdCdService.cs | 2 +- .../Services/Permissions/FilterService.cs | 2 +- .../Permissions/GlobalPermissionService.cs | 2 +- .../Permissions/PermissionsService.cs | 40 ++- .../Services/Pokemon/PokemonService.cs | 2 +- .../Services/Searches/AnimeSearchService.cs | 2 +- .../Services/Searches/SearchesService.cs | 2 +- .../Searches/StreamNotificationService.cs | 2 +- src/NadekoBot/Services/ServiceProvider.cs | 72 ++++- .../Services/Utility/CommandMapService.cs | 2 +- .../Services/Utility/ConverterService.cs | 2 +- .../Utility/MessageRepeaterService.cs | 5 +- .../Services/Utility/PatreonRewardsService.cs | 3 +- .../Services/Utility/RemindService.cs | 8 +- .../Services/Utility/StreamRoleService.cs | 3 +- .../Services/Utility/VerboseErrorsService.cs | 2 +- src/NadekoBot/ShardsCoordinator.cs | 1 - 74 files changed, 395 insertions(+), 965 deletions(-) delete mode 100644 src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs delete mode 100644 src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs delete mode 100644 src/NadekoBot/Services/ClashOfClans/Extensions.cs create mode 100644 src/NadekoBot/Services/INService.cs create mode 100644 src/NadekoBot/Services/Impl/StartingGuildsListService.cs diff --git a/src/NadekoBot/DataStructures/TypeReaders/BotCommandTypeReader.cs b/src/NadekoBot/DataStructures/TypeReaders/BotCommandTypeReader.cs index 8a415eae..d3e7b2c3 100644 --- a/src/NadekoBot/DataStructures/TypeReaders/BotCommandTypeReader.cs +++ b/src/NadekoBot/DataStructures/TypeReaders/BotCommandTypeReader.cs @@ -9,17 +9,10 @@ namespace NadekoBot.TypeReaders { public class CommandTypeReader : TypeReader { - private readonly CommandService _cmds; - private readonly CommandHandler _cmdHandler; - - public CommandTypeReader(CommandService cmds, CommandHandler cmdHandler) - { - _cmds = cmds; - _cmdHandler = cmdHandler; - } - - public override Task Read(ICommandContext context, string input, IServiceProvider _) + public override Task Read(ICommandContext context, string input, IServiceProvider services) { + var _cmds = ((INServiceProvider)services).GetService(); + var _cmdHandler = ((INServiceProvider)services).GetService(); input = input.ToUpperInvariant(); var prefix = _cmdHandler.GetPrefix(context.Guild); if (!input.StartsWith(prefix.ToUpperInvariant())) @@ -38,17 +31,12 @@ namespace NadekoBot.TypeReaders public class CommandOrCrTypeReader : CommandTypeReader { - private readonly CustomReactionsService _crs; - - public CommandOrCrTypeReader(CustomReactionsService crs, CommandService cmds, CommandHandler cmdHandler) : base(cmds, cmdHandler) - { - _crs = crs; - } - - public override async Task Read(ICommandContext context, string input, IServiceProvider _) + public override async Task Read(ICommandContext context, string input, IServiceProvider services) { input = input.ToUpperInvariant(); + var _crs = ((INServiceProvider)services).GetService(); + if (_crs.GlobalReactions.Any(x => x.Trigger.ToUpperInvariant() == input)) { return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input)); @@ -65,7 +53,7 @@ namespace NadekoBot.TypeReaders } } - var cmd = await base.Read(context, input, _); + var cmd = await base.Read(context, input, services); if (cmd.IsSuccess) { return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name)); diff --git a/src/NadekoBot/DataStructures/TypeReaders/GuildDateTimeTypeReader.cs b/src/NadekoBot/DataStructures/TypeReaders/GuildDateTimeTypeReader.cs index 7e394a21..8b6ea976 100644 --- a/src/NadekoBot/DataStructures/TypeReaders/GuildDateTimeTypeReader.cs +++ b/src/NadekoBot/DataStructures/TypeReaders/GuildDateTimeTypeReader.cs @@ -1,4 +1,5 @@ using Discord.Commands; +using NadekoBot.Services; using NadekoBot.Services.Administration; using System; using System.Threading.Tasks; @@ -7,15 +8,9 @@ namespace NadekoBot.TypeReaders { public class GuildDateTimeTypeReader : TypeReader { - private readonly GuildTimezoneService _gts; - - public GuildDateTimeTypeReader(GuildTimezoneService gts) - { - _gts = gts; - } - - public override Task Read(ICommandContext context, string input, IServiceProvider _) + public override Task Read(ICommandContext context, string input, IServiceProvider services) { + var _gts = (GuildTimezoneService)services.GetService(typeof(GuildTimezoneService)); if (!DateTime.TryParse(input, out var dt)) return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input string is in an incorrect format.")); diff --git a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs index 85f075fa..e3ca82b9 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] - [Priority(1)] + [Priority(0)] public async Task SetMuteRole([Remainder] string name) { name = name.Trim(); @@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] - [Priority(0)] + [Priority(1)] public Task SetMuteRole([Remainder] IRole role) => SetMuteRole(role.Name); @@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.MuteMembers)] - [Priority(1)] + [Priority(0)] public async Task Mute(IGuildUser user) { try @@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.MuteMembers)] - [Priority(0)] + [Priority(1)] public async Task Mute(int minutes, IGuildUser user) { if (minutes < 1 || minutes > 1440) diff --git a/src/NadekoBot/Modules/Administration/Commands/PrefixCommands.cs b/src/NadekoBot/Modules/Administration/Commands/PrefixCommands.cs index 8f8d4cec..012b26c5 100644 --- a/src/NadekoBot/Modules/Administration/Commands/PrefixCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/PrefixCommands.cs @@ -11,7 +11,7 @@ namespace NadekoBot.Modules.Administration public class PrefixCommands : NadekoSubmodule { [NadekoCommand, Usage, Description, Aliases] - [Priority(0)] + [Priority(1)] public new async Task Prefix() { await ReplyConfirmLocalized("prefix_current", Format.Code(_cmdHandler.GetPrefix(Context.Guild))).ConfigureAwait(false); @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.Administrator)] - [Priority(1)] + [Priority(0)] public new async Task Prefix([Remainder]string prefix) { if (string.IsNullOrWhiteSpace(prefix)) diff --git a/src/NadekoBot/Modules/Administration/Commands/PruneCommands.cs b/src/NadekoBot/Modules/Administration/Commands/PruneCommands.cs index a4086f82..44407c19 100644 --- a/src/NadekoBot/Modules/Administration/Commands/PruneCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/PruneCommands.cs @@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] [RequireUserPermission(ChannelPermission.ManageMessages)] [RequireBotPermission(GuildPermission.ManageMessages)] - [Priority(0)] + [Priority(1)] public async Task Prune(int count) { count++; @@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] [RequireUserPermission(ChannelPermission.ManageMessages)] [RequireBotPermission(GuildPermission.ManageMessages)] - [Priority(1)] + [Priority(0)] public async Task Prune(IGuildUser user, int count = 100) { if (user.Id == Context.User.Id) diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index 878b4a77..bf23d99b 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] - [Priority(1)] + [Priority(0)] public async Task SlowmodeWhitelist(IGuildUser user) { var siu = new SlowmodeIgnoredUser @@ -99,7 +99,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] - [Priority(0)] + [Priority(1)] public async Task SlowmodeWhitelist(IRole role) { var sir = new SlowmodeIgnoredRole diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 14a9e196..2205a1c8 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -289,7 +289,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireUserPermission(GuildPermission.ManageNicknames)] - [Priority(1)] + [Priority(0)] public async Task SetNick([Remainder] string newNick = null) { if (string.IsNullOrWhiteSpace(newNick)) @@ -303,7 +303,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireBotPermission(GuildPermission.ManageNicknames)] [RequireUserPermission(GuildPermission.ManageNicknames)] - [Priority(0)] + [Priority(1)] public async Task SetNick(IGuildUser gu, [Remainder] string newNick = null) { await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Administration/Commands/UserPunishCommands.cs b/src/NadekoBot/Modules/Administration/Commands/UserPunishCommands.cs index 8538c619..692c8502 100644 --- a/src/NadekoBot/Modules/Administration/Commands/UserPunishCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/UserPunishCommands.cs @@ -132,27 +132,27 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.BanMembers)] - [Priority(1)] + [Priority(2)] public Task Warnlog(int page, IGuildUser user) => Warnlog(page, user.Id); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [Priority(0)] + [Priority(3)] public Task Warnlog(IGuildUser user) => Context.User.Id == user.Id || ((IGuildUser)Context.User).GuildPermissions.BanMembers ? Warnlog(user.Id) : Task.CompletedTask; [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.BanMembers)] - [Priority(3)] + [Priority(0)] public Task Warnlog(int page, ulong userId) => InternalWarnlog(userId, page - 1); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.BanMembers)] - [Priority(2)] + [Priority(1)] public Task Warnlog(ulong userId) => InternalWarnlog(userId, 0); diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs deleted file mode 100644 index cb11dd29..00000000 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ /dev/null @@ -1,239 +0,0 @@ -using Discord.Commands; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Discord; -using NadekoBot.Attributes; -using NadekoBot.Services.Database.Models; -using NadekoBot.Extensions; -using NadekoBot.Services.ClashOfClans; - -namespace NadekoBot.Modules.ClashOfClans -{ - public class ClashOfClans : NadekoTopLevelModule - { - private readonly ClashOfClansService _service; - - public ClashOfClans(ClashOfClansService service) - { - _service = service; - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - [RequireUserPermission(GuildPermission.ManageMessages)] - public async Task CreateWar(int size, [Remainder] string enemyClan = null) - { - if (string.IsNullOrWhiteSpace(enemyClan)) - return; - - if (size < 10 || size > 50 || size % 5 != 0) - { - await ReplyErrorLocalized("invalid_size").ConfigureAwait(false); - return; - } - List wars; - if (!_service.ClashWars.TryGetValue(Context.Guild.Id, out wars)) - { - wars = new List(); - if (!_service.ClashWars.TryAdd(Context.Guild.Id, wars)) - return; - } - - - var cw = await _service.CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id); - - wars.Add(cw); - await ReplyErrorLocalized("war_created", _service.ShortPrint(cw)).ConfigureAwait(false); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task StartWar([Remainder] string number = null) - { - int num = 0; - int.TryParse(number, out num); - - var warsInfo = _service.GetWarInfo(Context.Guild, num); - if (warsInfo == null) - { - await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); - return; - } - var war = warsInfo.Item1[warsInfo.Item2]; - try - { - war.Start(); - await ReplyConfirmLocalized("war_started", _service.ShortPrint(war)).ConfigureAwait(false); - } - catch - { - await ReplyErrorLocalized("war_already_started", _service.ShortPrint(war)).ConfigureAwait(false); - } - _service.SaveWar(war); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task ListWar([Remainder] string number = null) - { - - // if number is null, print all wars in a short way - if (string.IsNullOrWhiteSpace(number)) - { - //check if there are any wars - List wars = null; - _service.ClashWars.TryGetValue(Context.Guild.Id, out wars); - if (wars == null || wars.Count == 0) - { - await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false); - return; - } - - var sb = new StringBuilder(); - sb.AppendLine("**-------------------------**"); - for (var i = 0; i < wars.Count; i++) - { - sb.AppendLine($"**#{i + 1}.** `{GetText("enemy")}:` **{wars[i].EnemyClan}**"); - sb.AppendLine($"\t\t`{GetText("size")}:` **{wars[i].Size} v {wars[i].Size}**"); - sb.AppendLine("**-------------------------**"); - } - await Context.Channel.SendConfirmAsync(GetText("list_active_wars"), sb.ToString()).ConfigureAwait(false); - return; - } - var num = 0; - int.TryParse(number, out num); - //if number is not null, print the war needed - var warsInfo = _service.GetWarInfo(Context.Guild, num); - if (warsInfo == null) - { - await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); - return; - } - var war = warsInfo.Item1[warsInfo.Item2]; - await Context.Channel.SendConfirmAsync(_service.Localize(war, "info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), _service.ToPrettyString(war)).ConfigureAwait(false); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task BaseCall(int number, int baseNumber, [Remainder] string other_name = null) - { - var warsInfo = _service.GetWarInfo(Context.Guild, number); - if (warsInfo == null || warsInfo.Item1.Count == 0) - { - await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); - return; - } - var usr = - string.IsNullOrWhiteSpace(other_name) ? - Context.User.Username : - other_name; - try - { - var war = warsInfo.Item1[warsInfo.Item2]; - _service.Call(war, usr, baseNumber - 1); - _service.SaveWar(war); - await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, _service.ShortPrint(war)).ConfigureAwait(false); - } - catch (Exception ex) - { - await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false); - } - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task CallFinish1(int number, int baseNumber = 0) - { - await FinishClaim(number, baseNumber - 1, 1); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task CallFinish2(int number, int baseNumber = 0) - { - await FinishClaim(number, baseNumber - 1, 2); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task CallFinish(int number, int baseNumber = 0) - { - await FinishClaim(number, baseNumber - 1); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task EndWar(int number) - { - var warsInfo = _service.GetWarInfo(Context.Guild, number); - if (warsInfo == null) - { - await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); - return; - } - var war = warsInfo.Item1[warsInfo.Item2]; - war.End(); - _service.SaveWar(war); - await ReplyConfirmLocalized("war_ended", _service.ShortPrint(warsInfo.Item1[warsInfo.Item2])).ConfigureAwait(false); - - warsInfo.Item1.RemoveAt(warsInfo.Item2); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - public async Task Unclaim(int number, [Remainder] string otherName = null) - { - var warsInfo = _service.GetWarInfo(Context.Guild, number); - if (warsInfo == null || warsInfo.Item1.Count == 0) - { - await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); - return; - } - var usr = - string.IsNullOrWhiteSpace(otherName) ? - Context.User.Username : - otherName; - try - { - var war = warsInfo.Item1[warsInfo.Item2]; - var baseNumber = _service.Uncall(war, usr); - _service.SaveWar(war); - await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, _service.ShortPrint(war)).ConfigureAwait(false); - } - catch (Exception ex) - { - await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false); - } - } - - private async Task FinishClaim(int number, int baseNumber, int stars = 3) - { - var warInfo = _service.GetWarInfo(Context.Guild, number); - if (warInfo == null || warInfo.Item1.Count == 0) - { - await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); - return; - } - var war = warInfo.Item1[warInfo.Item2]; - try - { - if (baseNumber == -1) - { - baseNumber = _service.FinishClaim(war, Context.User.Username, stars); - _service.SaveWar(war); - } - else - { - _service.FinishClaim(war, baseNumber, stars); - } - await ReplyConfirmLocalized("base_destroyed", baseNumber + 1, _service.ShortPrint(war)).ConfigureAwait(false); - } - catch (Exception ex) - { - await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false); - } - } - } -} \ No newline at end of file diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index dd6b162d..b80a5921 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -84,7 +84,7 @@ namespace NadekoBot.Modules.CustomReactions } [NadekoCommand, Usage, Description, Aliases] - [Priority(0)] + [Priority(1)] public async Task ListCustReact(int page = 1) { if (--page < 0 || page > 999) @@ -130,7 +130,7 @@ namespace NadekoBot.Modules.CustomReactions } [NadekoCommand, Usage, Description, Aliases] - [Priority(1)] + [Priority(0)] public async Task ListCustReact(All x) { CustomReaction[] customReactions; diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 2b15c79d..0a81a943 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -58,7 +58,7 @@ namespace NadekoBot.Modules.Gambling } [NadekoCommand, Usage, Description, Aliases] - [Priority(0)] + [Priority(1)] public async Task Roll(int num) { await InternalRoll(num, true).ConfigureAwait(false); @@ -66,21 +66,21 @@ namespace NadekoBot.Modules.Gambling [NadekoCommand, Usage, Description, Aliases] - [Priority(0)] + [Priority(1)] public async Task Rolluo(int num = 1) { await InternalRoll(num, false).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - [Priority(1)] + [Priority(0)] public async Task Roll(string arg) { await InternallDndRoll(arg, true).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - [Priority(1)] + [Priority(0)] public async Task Rolluo(string arg) { await InternallDndRoll(arg, false).ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index 02025444..962e24dc 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -197,12 +197,12 @@ namespace NadekoBot.Modules.Gambling private static readonly TimeSpan _divorceLimit = TimeSpan.FromHours(6); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [Priority(1)] + [Priority(0)] public Task Divorce([Remainder]IGuildUser target) => Divorce(target.Id); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [Priority(0)] + [Priority(1)] public async Task Divorce([Remainder]ulong targetId) { if (targetId == Context.User.Id) diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 500319ca..c8013f0d 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Gambling } [NadekoCommand, Usage, Description, Aliases] - [Priority(0)] + [Priority(1)] public async Task Cash([Remainder] IUser user = null) { if(user == null) @@ -62,7 +62,7 @@ namespace NadekoBot.Modules.Gambling } [NadekoCommand, Usage, Description, Aliases] - [Priority(1)] + [Priority(0)] public async Task Cash(ulong userId) { await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); @@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Gambling [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [OwnerOnly] - [Priority(2)] + [Priority(0)] public Task Award(int amount, [Remainder] IGuildUser usr) => Award(amount, usr.Id); @@ -107,7 +107,7 @@ namespace NadekoBot.Modules.Gambling [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [OwnerOnly] - [Priority(0)] + [Priority(2)] public async Task Award(int amount, [Remainder] IRole role) { var users = (await Context.Guild.GetUsersAsync()) diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index 4b15b72e..7d3162cc 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -82,14 +82,14 @@ namespace NadekoBot.Modules.Help await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - [Priority(1)] + [Priority(0)] public async Task H([Remainder] string fail) { await ReplyErrorLocalized("command_not_found").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - [Priority(0)] + [Priority(1)] public async Task H([Remainder] CommandInfo com = null) { var channel = Context.Channel; diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index b26095a3..1d84dcee 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -377,7 +377,7 @@ namespace NadekoBot.Modules.Music [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [Priority(0)] + [Priority(1)] public async Task SongRemove(int index) { if (index < 1) @@ -406,7 +406,7 @@ namespace NadekoBot.Modules.Music public enum All { All } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [Priority(1)] + [Priority(0)] public async Task SongRemove(All all) { var mp = _music.GetPlayerOrDefault(Context.Guild.Id); @@ -853,41 +853,6 @@ namespace NadekoBot.Modules.Music else await ReplyConfirmLocalized("rpl_disabled").ConfigureAwait(false); } - //todo readd goto - //[NadekoCommand, Usage, Description, Aliases] - //[RequireContext(ContextType.Guild)] - //public async Task Goto(int time) - //{ - // MusicPlayer musicPlayer; - // if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null) - // return; - // if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) - // return; - - // if (time < 0) - // return; - - // var currentSong = musicPlayer.CurrentSong; - - // if (currentSong == null) - // return; - - // //currentSong.PrintStatusMessage = false; - // var gotoSong = currentSong.Clone(); - // gotoSong.SkipTo = time; - // musicPlayer.AddSong(gotoSong, 0); - // musicPlayer.Next(); - - // var minutes = (time / 60).ToString(); - // var seconds = (time % 60).ToString(); - - // if (minutes.Length == 1) - // minutes = "0" + minutes; - // if (seconds.Length == 1) - // seconds = "0" + seconds; - - // await ReplyConfirmLocalized("skipped_to", minutes, seconds).ConfigureAwait(false); - //} [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 7ce29178..a5815bc6 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -192,10 +192,18 @@ namespace NadekoBot.Modules.NSFW if (imgObj == null) await ReplyErrorLocalized("not_found").ConfigureAwait(false); else - await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + { + var embed = new EmbedBuilder().WithOkColor() .WithDescription($"{Context.User} [{tag ?? "url"}]({imgObj}) ") - .WithImageUrl(imgObj.FileUrl) - .WithFooter(efb => efb.WithText(type.ToString()))).ConfigureAwait(false); + .WithFooter(efb => efb.WithText(type.ToString())); + + if (Uri.IsWellFormedUriString(imgObj.FileUrl, UriKind.Absolute)) + embed.WithImageUrl(imgObj.FileUrl); + else + _log.Error($"Image link from {type} is not a proper Url: {imgObj.FileUrl}"); + + await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); + } } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index c80bd2c1..1c32b091 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches } [NadekoCommand, Usage, Description, Aliases] - [Priority(1)] + [Priority(0)] public async Task Mal([Remainder] string name) { if (string.IsNullOrWhiteSpace(name)) @@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Searches [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [Priority(0)] + [Priority(1)] public Task Mal(IGuildUser usr) => Mal(usr.Username); [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs b/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs index a4b031a3..a3f37f60 100644 --- a/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches private const string _xkcdUrl = "https://xkcd.com"; [NadekoCommand, Usage, Description, Aliases] - [Priority(1)] + [Priority(0)] public async Task Xkcd(string arg = null) { if (arg?.ToLowerInvariant().Trim() == "latest") @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Searches } [NadekoCommand, Usage, Description, Aliases] - [Priority(0)] + [Priority(1)] public async Task Xkcd(int num) { if (num < 1) diff --git a/src/NadekoBot/Modules/Utility/Commands/Remind.cs b/src/NadekoBot/Modules/Utility/Commands/Remind.cs index 8957d1a8..e16a0486 100644 --- a/src/NadekoBot/Modules/Utility/Commands/Remind.cs +++ b/src/NadekoBot/Modules/Utility/Commands/Remind.cs @@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [Priority(0)] + [Priority(1)] public async Task Remind(MeOrHere meorhere, string timeStr, [Remainder] string message) { ulong target; @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] - [Priority(1)] + [Priority(0)] public async Task Remind(ITextChannel channel, string timeStr, [Remainder] string message) { var perms = ((IGuildUser)Context.User).GetPermissions((ITextChannel)channel); diff --git a/src/NadekoBot/Modules/Utility/Commands/RepeatCommands.cs b/src/NadekoBot/Modules/Utility/Commands/RepeatCommands.cs index 89619c1c..457e7b67 100644 --- a/src/NadekoBot/Modules/Utility/Commands/RepeatCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/RepeatCommands.cs @@ -63,7 +63,6 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] - [Priority(0)] public async Task RepeatRemove(int index) { if (!_service.RepeaterReady) @@ -103,7 +102,7 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] - [Priority(1)] + [Priority(0)] public async Task Repeat(int minutes, [Remainder] string message) { if (!_service.RepeaterReady) @@ -152,7 +151,7 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] - [Priority(0)] + [Priority(1)] public async Task Repeat(GuildDateTime gt, [Remainder] string message) { if (!_service.RepeaterReady) diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index e5b59a29..71a5a504 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -14,20 +14,12 @@ using System.Collections.Immutable; using System.Diagnostics; using NadekoBot.Services.Database.Models; using System.Threading; -using NadekoBot.Services.Searches; -using NadekoBot.Services.ClashOfClans; -using NadekoBot.Services.Music; -using NadekoBot.Services.CustomReactions; -using NadekoBot.Services.Games; -using NadekoBot.Services.Administration; -using NadekoBot.Services.Permissions; -using NadekoBot.Services.Utility; -using NadekoBot.Services.Help; using System.IO; -using NadekoBot.Services.Pokemon; using NadekoBot.DataStructures.ShardCom; using NadekoBot.DataStructures; using NadekoBot.Extensions; +using System.Collections.Generic; +using NadekoBot.Services.Database; namespace NadekoBot { @@ -35,6 +27,15 @@ namespace NadekoBot { private Logger _log; + public BotCredentials Credentials { get; } + + public DiscordSocketClient Client { get; } + public CommandService CommandService { get; } + + public DbService Db { get; } + public BotConfig BotConfig { get; } + public ImmutableArray AllGuildConfigs { get; private set; } + /* I don't know how to make this not be static * and keep the convenience of .WithOkColor * and .WithErrorColor extensions methods. @@ -44,23 +45,9 @@ namespace NadekoBot public static Color OkColor { get; private set; } public static Color ErrorColor { get; private set; } - public ImmutableArray AllGuildConfigs { get; private set; } - public BotConfig BotConfig { get; } - public DbService Db { get; } - public CommandService CommandService { get; } - public CommandHandler CommandHandler { get; private set; } - public Localization Localization { get; private set; } - public NadekoStrings Strings { get; private set; } - public StatsService Stats { get; private set; } - public ImagesService Images { get; } - public CurrencyService Currency { get; } - public GoogleApiService GoogleApi { get; } - - public DiscordSocketClient Client { get; } - public bool Ready { get; private set; } + public TaskCompletionSource Ready { get; private set; } = new TaskCompletionSource(); public INServiceProvider Services { get; private set; } - public BotCredentials Credentials { get; } public int ShardId { get; } public ShardsCoordinator ShardCoord { get; private set; } @@ -72,26 +59,14 @@ namespace NadekoBot if (shardId < 0) throw new ArgumentOutOfRangeException(nameof(shardId)); - ShardId = shardId; - LogSetup.SetupLogger(); _log = LogManager.GetCurrentClassLogger(); TerribleElevatedPermissionCheck(); + ShardId = shardId; + Credentials = new BotCredentials(); - - port = port ?? Credentials.ShardRunPort; - _comClient = new ShardComClient(port.Value); - Db = new DbService(Credentials); - - using (var uow = Db.UnitOfWork) - { - BotConfig = uow.BotConfig.GetOrCreate(); - OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); - ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16)); - } - Client = new DiscordSocketClient(new DiscordSocketConfig { MessageCacheSize = 10, @@ -101,16 +76,21 @@ namespace NadekoBot ShardId = shardId, AlwaysDownloadUsers = false, }); - CommandService = new CommandService(new CommandServiceConfig() { CaseSensitiveCommands = false, DefaultRunMode = RunMode.Sync, }); - - Images = new ImagesService(); - Currency = new CurrencyService(BotConfig, Db); - GoogleApi = new GoogleApiService(Credentials); + + port = port ?? Credentials.ShardRunPort; + _comClient = new ShardComClient(port.Value); + + using (var uow = Db.UnitOfWork) + { + BotConfig = uow.BotConfig.GetOrCreate(); + OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); + ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16)); + } SetupShard(shardId, parentProcessId, port.Value); @@ -145,141 +125,35 @@ namespace NadekoBot using (var uow = Db.UnitOfWork) { AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray(); - - Localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db); - Strings = new NadekoStrings(Localization); - CommandHandler = new CommandHandler(Client, Db, BotConfig, AllGuildConfigs, CommandService, Credentials, this); - Stats = new StatsService(Client, CommandHandler, Credentials, ShardCoord); - var soundcloudApiService = new SoundCloudApiService(Credentials); - - #region help - var helpService = new HelpService(BotConfig, CommandHandler, Strings); - #endregion - - //module services - //todo 90 - autodiscover, DI, and add instead of manual like this - #region utility - var remindService = new RemindService(Client, BotConfig, Db, startingGuildIdList, uow); - var repeaterService = new MessageRepeaterService(this, Client, AllGuildConfigs); - var converterService = new ConverterService(Client, Db); - var commandMapService = new CommandMapService(AllGuildConfigs); - var patreonRewardsService = new PatreonRewardsService(Credentials, Db, Currency, Client); - var verboseErrorsService = new VerboseErrorsService(AllGuildConfigs, Db, CommandHandler, helpService); - var pruneService = new PruneService(); - var streamRoleService = new StreamRoleService(Client, Db, AllGuildConfigs); - #endregion - - #region permissions - var permissionsService = new PermissionService(Client, Db, BotConfig, CommandHandler, Strings); - var blacklistService = new BlacklistService(BotConfig); - var cmdcdsService = new CmdCdService(AllGuildConfigs); - var filterService = new FilterService(Client, AllGuildConfigs); - var globalPermsService = new GlobalPermissionService(BotConfig); - #endregion - - #region Searches - var searchesService = new SearchesService(Client, GoogleApi, Db); - var streamNotificationService = new StreamNotificationService(Db, Client, Strings); - var animeSearchService = new AnimeSearchService(); - #endregion - - var clashService = new ClashOfClansService(Client, Db, Localization, Strings, uow, startingGuildIdList); - var musicService = new MusicService(Client, GoogleApi, Strings, Localization, Db, soundcloudApiService, Credentials, AllGuildConfigs); - var crService = new CustomReactionsService(permissionsService, Db, Strings, Client, CommandHandler, BotConfig, uow); - - #region Games - var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, Strings, Images, CommandHandler); - var chatterBotService = new ChatterBotService(Client, permissionsService, AllGuildConfigs, CommandHandler, Strings); - var pollService = new PollService(Client, Strings); - #endregion - - #region administration - var administrationService = new AdministrationService(AllGuildConfigs, CommandHandler); - var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, Db); - var selfService = new SelfService(Client, this, CommandHandler, Db, BotConfig, Localization, Strings, Credentials); - var vcRoleService = new VcRoleService(Client, AllGuildConfigs, Db); - var vPlusTService = new VplusTService(Client, AllGuildConfigs, Strings, Db); - var muteService = new MuteService(Client, AllGuildConfigs, Db); - var ratelimitService = new SlowmodeService(Client, AllGuildConfigs); - var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService); - var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService, Db); - var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs); - var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs); - var guildTimezoneService = new GuildTimezoneService(Client, AllGuildConfigs, Db); - var logCommandService = new LogCommandService(Client, Strings, AllGuildConfigs, Db, muteService, protectionService, guildTimezoneService); - #endregion - - #region pokemon - var pokemonService = new PokemonService(); - #endregion + var localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), Db); //initialize Services Services = new NServiceProvider.ServiceProviderBuilder() - .Add(Localization) - .Add(Stats) - .Add(Images) - .Add(GoogleApi) - .Add(Stats) - .Add(Credentials) - .Add(CommandService) - .Add(Strings) - .Add(Client) - .Add(BotConfig) - .Add(Currency) - .Add(CommandHandler) - .Add(Db) - //modules - .Add(commandMapService) - .Add(remindService) - .Add(repeaterService) - .Add(converterService) - .Add(verboseErrorsService) - .Add(patreonRewardsService) - .Add(pruneService) - .Add(streamRoleService) - .Add(searchesService) - .Add(streamNotificationService) - .Add(animeSearchService) - .Add(clashService) - .Add(musicService) - .Add(greetSettingsService) - .Add(crService) - .Add(helpService) - .Add(gamesService) - .Add(chatterBotService) - .Add(pollService) - .Add(administrationService) - .Add(selfService) - .Add(vcRoleService) - .Add(vPlusTService) - .Add(muteService) - .Add(ratelimitService) - .Add(playingRotateService) - .Add(gameVcService) - .Add(autoAssignRoleService) - .Add(protectionService) - .Add(logCommandService) - .Add(guildTimezoneService) - .Add(permissionsService) - .Add(blacklistService) - .Add(cmdcdsService) - .Add(filterService) - .Add(globalPermsService) - .Add(pokemonService) - .Add(this) + .AddManual(Credentials) + .AddManual(Db) + .AddManual(BotConfig) + .AddManual(Client) + .AddManual(CommandService) + .AddManual(localization) + .AddManual>(AllGuildConfigs) //todo wrap this + .AddManual(this) + .AddManual(uow) + .AddManual(ShardCoord) + .LoadFrom(Assembly.GetEntryAssembly()) .Build(); - CommandHandler.AddServices(Services); + var commandHandler = Services.GetService(); + commandHandler.AddServices(Services); //setup typereaders CommandService.AddTypeReader(new PermissionActionTypeReader()); - CommandService.AddTypeReader(new CommandTypeReader(CommandService, CommandHandler)); - CommandService.AddTypeReader(new CommandOrCrTypeReader(crService, CommandService, CommandHandler)); + CommandService.AddTypeReader(new CommandTypeReader()); + CommandService.AddTypeReader(new CommandOrCrTypeReader()); CommandService.AddTypeReader(new ModuleTypeReader(CommandService)); CommandService.AddTypeReader(new ModuleOrCrTypeReader(CommandService)); CommandService.AddTypeReader(new GuildTypeReader(Client)); - CommandService.AddTypeReader(new GuildDateTimeTypeReader(guildTimezoneService)); + CommandService.AddTypeReader(new GuildDateTimeTypeReader()); } } @@ -382,7 +256,7 @@ namespace NadekoBot .Where(x => x.Preconditions.Any(y => y.GetType() == typeof(NoPublicBot))) .ForEach(x => CommandService.RemoveModuleAsync(x)); - Ready = true; + Ready.TrySetResult(true); _log.Info($"Shard {ShardId} ready."); //_log.Info(await stats.Print().ConfigureAwait(false)); } diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index 22771b48..b0d9d8b0 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -55,7 +55,7 @@ - + diff --git a/src/NadekoBot/Services/Administration/AdministrationService.cs b/src/NadekoBot/Services/Administration/AdministrationService.cs index 3ead9cc3..3fe34164 100644 --- a/src/NadekoBot/Services/Administration/AdministrationService.cs +++ b/src/NadekoBot/Services/Administration/AdministrationService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class AdministrationService + public class AdministrationService : INService { public readonly ConcurrentHashSet DeleteMessagesOnCommand; private readonly Logger _log; diff --git a/src/NadekoBot/Services/Administration/AutoAssignRoleService.cs b/src/NadekoBot/Services/Administration/AutoAssignRoleService.cs index 7c8b8711..bba16d44 100644 --- a/src/NadekoBot/Services/Administration/AutoAssignRoleService.cs +++ b/src/NadekoBot/Services/Administration/AutoAssignRoleService.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class AutoAssignRoleService + public class AutoAssignRoleService : INService { private readonly Logger _log; private readonly DiscordSocketClient _client; diff --git a/src/NadekoBot/Services/Administration/GameVoiceChannelService.cs b/src/NadekoBot/Services/Administration/GameVoiceChannelService.cs index 52ea08f7..b7389bed 100644 --- a/src/NadekoBot/Services/Administration/GameVoiceChannelService.cs +++ b/src/NadekoBot/Services/Administration/GameVoiceChannelService.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class GameVoiceChannelService + public class GameVoiceChannelService : INService { public readonly ConcurrentHashSet GameVoiceChannels = new ConcurrentHashSet(); diff --git a/src/NadekoBot/Services/Administration/GuildTimezoneService.cs b/src/NadekoBot/Services/Administration/GuildTimezoneService.cs index 66736679..c15eb534 100644 --- a/src/NadekoBot/Services/Administration/GuildTimezoneService.cs +++ b/src/NadekoBot/Services/Administration/GuildTimezoneService.cs @@ -9,7 +9,7 @@ using Discord.WebSocket; namespace NadekoBot.Services.Administration { - public class GuildTimezoneService + public class GuildTimezoneService : INService { //hack >.> public static ConcurrentDictionary AllServices { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Services/Administration/LogCommandService.cs b/src/NadekoBot/Services/Administration/LogCommandService.cs index 9b3b432c..d5221eb2 100644 --- a/src/NadekoBot/Services/Administration/LogCommandService.cs +++ b/src/NadekoBot/Services/Administration/LogCommandService.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class LogCommandService + public class LogCommandService : INService { private readonly DiscordSocketClient _client; diff --git a/src/NadekoBot/Services/Administration/MuteService.cs b/src/NadekoBot/Services/Administration/MuteService.cs index 2b8c3fb9..04042e5f 100644 --- a/src/NadekoBot/Services/Administration/MuteService.cs +++ b/src/NadekoBot/Services/Administration/MuteService.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Services.Administration All } - public class MuteService + public class MuteService : INService { public ConcurrentDictionary GuildMuteRoles { get; } public ConcurrentDictionary> MutedUsers { get; } diff --git a/src/NadekoBot/Services/Administration/PlayingRotateService.cs b/src/NadekoBot/Services/Administration/PlayingRotateService.cs index 78e946c8..45603150 100644 --- a/src/NadekoBot/Services/Administration/PlayingRotateService.cs +++ b/src/NadekoBot/Services/Administration/PlayingRotateService.cs @@ -9,7 +9,7 @@ using System.Threading; namespace NadekoBot.Services.Administration { - public class PlayingRotateService + public class PlayingRotateService : INService { private readonly Timer _t; private readonly DiscordSocketClient _client; diff --git a/src/NadekoBot/Services/Administration/ProtectionService.cs b/src/NadekoBot/Services/Administration/ProtectionService.cs index 6a2ae99b..6996b3a1 100644 --- a/src/NadekoBot/Services/Administration/ProtectionService.cs +++ b/src/NadekoBot/Services/Administration/ProtectionService.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class ProtectionService + public class ProtectionService : INService { public readonly ConcurrentDictionary AntiRaidGuilds = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Services/Administration/PruneService.cs b/src/NadekoBot/Services/Administration/PruneService.cs index e1078102..59da41b5 100644 --- a/src/NadekoBot/Services/Administration/PruneService.cs +++ b/src/NadekoBot/Services/Administration/PruneService.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class PruneService + public class PruneService : INService { //channelids where prunes are currently occuring private ConcurrentHashSet _pruningGuilds = new ConcurrentHashSet(); diff --git a/src/NadekoBot/Services/Administration/RatelimitService.cs b/src/NadekoBot/Services/Administration/RatelimitService.cs index a0f4171f..0c68accd 100644 --- a/src/NadekoBot/Services/Administration/RatelimitService.cs +++ b/src/NadekoBot/Services/Administration/RatelimitService.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class SlowmodeService : IEarlyBlocker + public class SlowmodeService : IEarlyBlocker, INService { public ConcurrentDictionary RatelimitingChannels = new ConcurrentDictionary(); public ConcurrentDictionary> IgnoredRoles = new ConcurrentDictionary>(); diff --git a/src/NadekoBot/Services/Administration/SelfService.cs b/src/NadekoBot/Services/Administration/SelfService.cs index 4134393b..8b567c42 100644 --- a/src/NadekoBot/Services/Administration/SelfService.cs +++ b/src/NadekoBot/Services/Administration/SelfService.cs @@ -12,7 +12,7 @@ using System.Collections.Generic; namespace NadekoBot.Services.Administration { - public class SelfService : ILateExecutor + public class SelfService : ILateExecutor, INService { public volatile bool ForwardDMs; public volatile bool ForwardDMsToAllOwners; @@ -44,8 +44,7 @@ namespace NadekoBot.Services.Administration var _ = Task.Run(async () => { - while (!bot.Ready) - await Task.Delay(1000); + await bot.Ready.Task.ConfigureAwait(false); foreach (var cmd in bc.StartupCommands) { @@ -56,8 +55,7 @@ namespace NadekoBot.Services.Administration var ___ = Task.Run(async () => { - while (!bot.Ready) - await Task.Delay(1000); + await bot.Ready.Task.ConfigureAwait(false); await Task.Delay(5000); diff --git a/src/NadekoBot/Services/Administration/VcRoleService.cs b/src/NadekoBot/Services/Administration/VcRoleService.cs index 10252833..e1d8e5d4 100644 --- a/src/NadekoBot/Services/Administration/VcRoleService.cs +++ b/src/NadekoBot/Services/Administration/VcRoleService.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class VcRoleService + public class VcRoleService : INService { private readonly Logger _log; private readonly DbService _db; diff --git a/src/NadekoBot/Services/Administration/VplusTService.cs b/src/NadekoBot/Services/Administration/VplusTService.cs index b4e3122d..d61bb9f8 100644 --- a/src/NadekoBot/Services/Administration/VplusTService.cs +++ b/src/NadekoBot/Services/Administration/VplusTService.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { - public class VplusTService + public class VplusTService : INService { private readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); diff --git a/src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs b/src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs deleted file mode 100644 index dcdd6376..00000000 --- a/src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs +++ /dev/null @@ -1,262 +0,0 @@ -using Discord; -using Discord.WebSocket; -using NadekoBot.Extensions; -using NadekoBot.Services.Database; -using NadekoBot.Services.Database.Models; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NadekoBot.Services.ClashOfClans -{ - // todo 99 rewrite, just made this compile, it's a complete mess. A lot of the things here should actually be in the actual module. - // service should just handle the state, module should print out what happened, so anything that has to do with strings - // shouldn't be here - public class ClashOfClansService - { - private readonly DiscordSocketClient _client; - private readonly DbService _db; - private readonly ILocalization _localization; - private readonly NadekoStrings _strings; - private readonly Timer checkWarTimer; - - public ConcurrentDictionary> ClashWars { get; set; } - - public ClashOfClansService(DiscordSocketClient client, DbService db, - ILocalization localization, NadekoStrings strings, IUnitOfWork uow, - List guilds) - { - _client = client; - _db = db; - _localization = localization; - _strings = strings; - - ClashWars = new ConcurrentDictionary>( - uow.ClashOfClans - .GetAllWars(guilds) - .Select(cw => - { - cw.Channel = _client.GetGuild(cw.GuildId)? - .GetTextChannel(cw.ChannelId); - return cw; - }) - .Where(cw => cw.Channel != null) - .GroupBy(cw => cw.GuildId) - .ToDictionary(g => g.Key, g => g.ToList())); - - checkWarTimer = new Timer(async _ => - { - foreach (var kvp in ClashWars) - { - foreach (var war in kvp.Value) - { - try { await CheckWar(TimeSpan.FromHours(2), war).ConfigureAwait(false); } catch { } - } - } - }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); - } - - private async Task CheckWar(TimeSpan callExpire, ClashWar war) - { - var Bases = war.Bases; - for (var i = 0; i < Bases.Count; i++) - { - var callUser = Bases[i].CallUser; - if (callUser == null) continue; - if ((!Bases[i].BaseDestroyed) && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire) - { - if (Bases[i].Stars != 3) - Bases[i].BaseDestroyed = true; - else - Bases[i] = null; - try - { - SaveWar(war); - await war.Channel.SendErrorAsync(_strings.GetText("claim_expired", - _localization.GetCultureInfo(war.Channel.GuildId), - typeof(ClashOfClansService).Name.ToLowerInvariant(), - Format.Bold(Bases[i].CallUser), - ShortPrint(war))); - } - catch { } - } - } - } - - public Tuple, int> GetWarInfo(IGuild guild, int num) - { - List wars = null; - ClashWars.TryGetValue(guild.Id, out wars); - if (wars == null || wars.Count == 0) - { - return null; - } - // get the number of the war - else if (num < 1 || num > wars.Count) - { - return null; - } - num -= 1; - //get the actual war - return new Tuple, int>(wars, num); - } - - public async Task CreateWar(string enemyClan, int size, ulong serverId, ulong channelId) - { - var channel = _client.GetGuild(serverId)?.GetTextChannel(channelId); - using (var uow = _db.UnitOfWork) - { - var cw = new ClashWar - { - EnemyClan = enemyClan, - Size = size, - Bases = new List(size), - GuildId = serverId, - ChannelId = channelId, - Channel = channel, - }; - cw.Bases.Capacity = size; - for (int i = 0; i < size; i++) - { - cw.Bases.Add(new ClashCaller() - { - CallUser = null, - SequenceNumber = i, - }); - } - uow.ClashOfClans.Add(cw); - await uow.CompleteAsync(); - return cw; - } - } - - public void SaveWar(ClashWar cw) - { - if (cw.WarState == StateOfWar.Ended) - { - using (var uow = _db.UnitOfWork) - { - uow.ClashOfClans.Remove(cw); - uow.CompleteAsync(); - } - return; - } - - using (var uow = _db.UnitOfWork) - { - uow.ClashOfClans.Update(cw); - uow.CompleteAsync(); - } - } - - public void Call(ClashWar cw, string u, int baseNumber) - { - if (baseNumber < 0 || baseNumber >= cw.Bases.Count) - throw new ArgumentException(Localize(cw, "invalid_base_number")); - if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3) - throw new ArgumentException(Localize(cw, "base_already_claimed")); - for (var i = 0; i < cw.Bases.Count; i++) - { - if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u) - throw new ArgumentException(Localize(cw, "claimed_other", u, i + 1)); - } - - var cc = cw.Bases[baseNumber]; - cc.CallUser = u.Trim(); - cc.TimeAdded = DateTime.UtcNow; - cc.BaseDestroyed = false; - } - - public int FinishClaim(ClashWar cw, string user, int stars = 3) - { - user = user.Trim(); - for (var i = 0; i < cw.Bases.Count; i++) - { - if (cw.Bases[i]?.BaseDestroyed != false || cw.Bases[i]?.CallUser != user) continue; - cw.Bases[i].BaseDestroyed = true; - cw.Bases[i].Stars = stars; - return i; - } - throw new InvalidOperationException(Localize(cw, "not_partic_or_destroyed", user)); - } - - public void FinishClaim(ClashWar cw, int index, int stars = 3) - { - if (index < 0 || index > cw.Bases.Count) - throw new ArgumentOutOfRangeException(nameof(index)); - var toFinish = cw.Bases[index]; - if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(Localize(cw, "base_already_destroyed")); - if (toFinish.CallUser == null) throw new InvalidOperationException(Localize(cw, "base_already_unclaimed")); - toFinish.BaseDestroyed = true; - toFinish.Stars = stars; - } - - public int Uncall(ClashWar cw, string user) - { - user = user.Trim(); - for (var i = 0; i < cw.Bases.Count; i++) - { - if (cw.Bases[i]?.CallUser != user) continue; - cw.Bases[i].CallUser = null; - return i; - } - throw new InvalidOperationException(Localize(cw, "not_partic")); - } - - public string ShortPrint(ClashWar cw) => - $"`{cw.EnemyClan}` ({cw.Size} v {cw.Size})"; - - public string ToPrettyString(ClashWar cw) - { - var sb = new StringBuilder(); - - if (cw.WarState == StateOfWar.Created) - sb.AppendLine("`not started`"); - var twoHours = new TimeSpan(2, 0, 0); - for (var i = 0; i < cw.Bases.Count; i++) - { - if (cw.Bases[i].CallUser == null) - { - sb.AppendLine($"`{i + 1}.` ❌*{Localize(cw, "not_claimed")}*"); - } - else - { - if (cw.Bases[i].BaseDestroyed) - { - sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {new string('⭐', cw.Bases[i].Stars)}"); - } - else - { - var left = (cw.WarState == StateOfWar.Started) ? twoHours - (DateTime.UtcNow - cw.Bases[i].TimeAdded) : twoHours; - if (cw.Bases[i].Stars == 3) - { - sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); - } - else - { - sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left {new string('⭐', cw.Bases[i].Stars)} {string.Concat(Enumerable.Repeat("🔸", 3 - cw.Bases[i].Stars))}"); - } - } - } - - } - return sb.ToString(); - } - - public string Localize(ClashWar cw, string key, params object[] replacements) - { - return string.Format(Localize(cw, key), replacements); - } - - public string Localize(ClashWar cw, string key) - { - return _strings.GetText(key, - _localization.GetCultureInfo(cw.Channel?.GuildId), - "ClashOfClans".ToLowerInvariant()); - } - } -} diff --git a/src/NadekoBot/Services/ClashOfClans/Extensions.cs b/src/NadekoBot/Services/ClashOfClans/Extensions.cs deleted file mode 100644 index 635557cf..00000000 --- a/src/NadekoBot/Services/ClashOfClans/Extensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -using NadekoBot.Services.Database.Models; -using System; -using System.Linq; - -namespace NadekoBot.Services.ClashOfClans -{ - public static class Extensions - { - public static void ResetTime(this ClashCaller c) - { - c.TimeAdded = DateTime.UtcNow; - } - - public static void Destroy(this ClashCaller c) - { - c.BaseDestroyed = true; - } - - public static void End(this ClashWar cw) - { - //Ended = true; - cw.WarState = StateOfWar.Ended; - } - - public static void Start(this ClashWar cw) - { - if (cw.WarState == StateOfWar.Started) - throw new InvalidOperationException("war_already_started"); - //if (Started) - // throw new InvalidOperationException(); - //Started = true; - cw.WarState = StateOfWar.Started; - cw.StartedAt = DateTime.UtcNow; - foreach (var b in cw.Bases.Where(b => b.CallUser != null)) - { - b.ResetTime(); - } - } - } -} \ No newline at end of file diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 58acff4d..81ecb073 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -24,7 +24,8 @@ namespace NadekoBot.Services public int GetHashCode(IGuildUser obj) => obj.Id.GetHashCode(); } - public class CommandHandler + + public class CommandHandler : INService { public const int GlobalCommandsCooldown = 750; @@ -189,7 +190,7 @@ namespace NadekoBot.Services { try { - if (msg.Author.IsBot || !_bot.Ready) //no bots, wait until bot connected and initialized + if (msg.Author.IsBot || !_bot.Ready.Task.IsCompleted) //no bots, wait until bot connected and initialized return; if (!(msg is SocketUserMessage usrMsg)) @@ -296,83 +297,124 @@ namespace NadekoBot.Services => ExecuteCommand(context, input.Substring(argPos), serviceProvider, multiMatchHandling); - public async Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommand(CommandContext context, string input, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) + public async Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommand(CommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { var searchResult = _commandService.Search(context, input); if (!searchResult.IsSuccess) return (false, null, null); var commands = searchResult.Commands; - for (int i = commands.Count - 1; i >= 0; i--) + var preconditionResults = new Dictionary(); + + foreach (var match in commands) { - var preconditionResult = await commands[i].CheckPreconditionsAsync(context, serviceProvider).ConfigureAwait(false); - if (!preconditionResult.IsSuccess) - { - return (false, preconditionResult.ErrorReason, commands[i].Command); - } - - var parseResult = await commands[i].ParseAsync(context, searchResult, preconditionResult).ConfigureAwait(false); - if (!parseResult.IsSuccess) - { - if (parseResult.Error == CommandError.MultipleMatches) - { - TypeReaderValue[] argList, paramList; - switch (multiMatchHandling) - { - case MultiMatchHandling.Best: - argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToArray(); - paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToArray(); - parseResult = ParseResult.FromSuccess(argList, paramList); - break; - } - } - - if (!parseResult.IsSuccess) - { - if (commands.Count == 1) - return (false, parseResult.ErrorReason, commands[i].Command); - else - continue; - } - } - - var cmd = commands[i].Command; - - // Bot will ignore commands which are ran more often than what specified by - // GlobalCommandsCooldown constant (miliseconds) - if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) - return (false, null, commands[i].Command); - //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown."); - - var commandName = cmd.Aliases.First(); - foreach (var svc in _services) - { - if (svc is ILateBlocker exec && - await exec.TryBlockLate(_client, context.Message, context.Guild, context.Channel, context.User, cmd.Module.GetTopLevelModule().Name, commandName).ConfigureAwait(false)) - { - _log.Info("Late blocking User [{0}] Command: [{1}] in [{2}]", context.User, commandName, svc.GetType().Name); - return (false, null, commands[i].Command); - } - } - - var execResult = (ExecuteResult)(await commands[i].ExecuteAsync(context, parseResult, serviceProvider)); - if (execResult.Exception != null && (!(execResult.Exception is HttpException he) || he.DiscordCode != 50013)) - { - lock (errorLogLock) - { - var now = DateTime.Now; - File.AppendAllText($"./command_errors_{now:yyyy-MM-dd}.txt", - $"[{now:HH:mm-yyyy-MM-dd}]" + Environment.NewLine - + execResult.Exception.ToString() + Environment.NewLine - + "------" + Environment.NewLine); - _log.Warn(execResult.Exception); - } - } - return (true, null, commands[i].Command); + preconditionResults[match] = await match.Command.CheckPreconditionsAsync(context, services).ConfigureAwait(false); } - return (false, null, null); - //return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); + var successfulPreconditions = preconditionResults + .Where(x => x.Value.IsSuccess) + .ToArray(); + + if (successfulPreconditions.Length == 0) + { + //All preconditions failed, return the one from the highest priority command + var bestCandidate = preconditionResults + .OrderByDescending(x => x.Key.Command.Priority) + .FirstOrDefault(x => !x.Value.IsSuccess); + return (false, bestCandidate.Value.ErrorReason, commands[0].Command); + } + + var parseResultsDict = new Dictionary(); + foreach (var pair in successfulPreconditions) + { + var parseResult = await pair.Key.ParseAsync(context, searchResult, pair.Value, services).ConfigureAwait(false); + + if (parseResult.Error == CommandError.MultipleMatches) + { + IReadOnlyList argList, paramList; + switch (multiMatchHandling) + { + case MultiMatchHandling.Best: + argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray(); + paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray(); + parseResult = ParseResult.FromSuccess(argList, paramList); + break; + } + } + + parseResultsDict[pair.Key] = parseResult; + } + // Calculates the 'score' of a command given a parse result + float CalculateScore(CommandMatch match, ParseResult parseResult) + { + float argValuesScore = 0, paramValuesScore = 0; + + if (match.Command.Parameters.Count > 0) + { + var argValuesSum = parseResult.ArgValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; + var paramValuesSum = parseResult.ParamValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; + + argValuesScore = argValuesSum / match.Command.Parameters.Count; + paramValuesScore = paramValuesSum / match.Command.Parameters.Count; + } + + var totalArgsScore = (argValuesScore + paramValuesScore) / 2; + return match.Command.Priority + totalArgsScore * 0.99f; + } + + //Order the parse results by their score so that we choose the most likely result to execute + var parseResults = parseResultsDict + .OrderByDescending(x => CalculateScore(x.Key, x.Value)); + + var successfulParses = parseResults + .Where(x => x.Value.IsSuccess) + .ToArray(); + + if (successfulParses.Length == 0) + { + //All parses failed, return the one from the highest priority command, using score as a tie breaker + var bestMatch = parseResults + .FirstOrDefault(x => !x.Value.IsSuccess); + return (false, bestMatch.Value.ErrorReason, commands[0].Command); + } + + var cmd = successfulParses[0].Key.Command; + + // Bot will ignore commands which are ran more often than what specified by + // GlobalCommandsCooldown constant (miliseconds) + if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) + return (false, null, cmd); + //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown."); + + var commandName = cmd.Aliases.First(); + foreach (var svc in _services) + { + if (svc is ILateBlocker exec && + await exec.TryBlockLate(_client, context.Message, context.Guild, context.Channel, context.User, cmd.Module.GetTopLevelModule().Name, commandName).ConfigureAwait(false)) + { + _log.Info("Late blocking User [{0}] Command: [{1}] in [{2}]", context.User, commandName, svc.GetType().Name); + return (false, null, cmd); + } + } + + //If we get this far, at least one parse was successful. Execute the most likely overload. + var chosenOverload = successfulParses[0]; + var execResult = (ExecuteResult)await chosenOverload.Key.ExecuteAsync(context, chosenOverload.Value, services).ConfigureAwait(false); + + if (execResult.Exception != null && (!(execResult.Exception is HttpException he) || he.DiscordCode != 50013)) + { + lock (errorLogLock) + { + var now = DateTime.Now; + File.AppendAllText($"./command_errors_{now:yyyy-MM-dd}.txt", + $"[{now:HH:mm-yyyy-MM-dd}]" + Environment.NewLine + + execResult.Exception.ToString() + Environment.NewLine + + "------" + Environment.NewLine); + _log.Warn(execResult.Exception); + } + } + + return (true, null, cmd); } private readonly object errorLogLock = new object(); diff --git a/src/NadekoBot/Services/CurrencyService.cs b/src/NadekoBot/Services/CurrencyService.cs index 99f1644a..fb664d62 100644 --- a/src/NadekoBot/Services/CurrencyService.cs +++ b/src/NadekoBot/Services/CurrencyService.cs @@ -7,7 +7,7 @@ using NadekoBot.Services.Database; namespace NadekoBot.Services { - public class CurrencyService + public class CurrencyService : INService { private readonly BotConfig _config; private readonly DbService _db; diff --git a/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs b/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs index c64f5c81..2e068cde 100644 --- a/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs +++ b/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs @@ -14,7 +14,7 @@ using NadekoBot.Services.Database; namespace NadekoBot.Services.CustomReactions { - public class CustomReactionsService : IEarlyBlockingExecutor + public class CustomReactionsService : IEarlyBlockingExecutor, INService { public CustomReaction[] GlobalReactions = new CustomReaction[] { }; public ConcurrentDictionary GuildReactions { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Services/Database/Models/BotConfig.cs b/src/NadekoBot/Services/Database/Models/BotConfig.cs index 0fa46b84..d765b813 100644 --- a/src/NadekoBot/Services/Database/Models/BotConfig.cs +++ b/src/NadekoBot/Services/Database/Models/BotConfig.cs @@ -65,7 +65,7 @@ Nadeko Support Server: https://discord.gg/nadekobot"; public List StartupCommands { get; set; } public HashSet BlockedCommands { get; set; } public HashSet BlockedModules { get; set; } - public int PermissionVersion { get; set; } = 1; + public int PermissionVersion { get; set; } public string DefaultPrefix { get; set; } = "."; public bool CustomReactionsStartWith { get; set; } = false; } diff --git a/src/NadekoBot/Services/Database/Repositories/IReminderRepository.cs b/src/NadekoBot/Services/Database/Repositories/IReminderRepository.cs index 07eec33f..a4812775 100644 --- a/src/NadekoBot/Services/Database/Repositories/IReminderRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/IReminderRepository.cs @@ -6,6 +6,6 @@ namespace NadekoBot.Services.Database.Repositories { public interface IReminderRepository : IRepository { - IEnumerable GetIncludedReminders(List guildIds); + IEnumerable GetIncludedReminders(IEnumerable guildIds); } } diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/ReminderRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/ReminderRepository.cs index b1a0e2a0..b2b13a2b 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/ReminderRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/ReminderRepository.cs @@ -12,7 +12,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl { } - public IEnumerable GetIncludedReminders(List guildIds) + public IEnumerable GetIncludedReminders(IEnumerable guildIds) { return _set.Where(x => guildIds.Contains((long)x.ServerId)).ToList(); } diff --git a/src/NadekoBot/Services/Games/ChatterbotService.cs b/src/NadekoBot/Services/Games/ChatterbotService.cs index 7f9c8251..0f067beb 100644 --- a/src/NadekoBot/Services/Games/ChatterbotService.cs +++ b/src/NadekoBot/Services/Games/ChatterbotService.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Games { - public class ChatterBotService : IEarlyBlockingExecutor + public class ChatterBotService : IEarlyBlockingExecutor, INService { private readonly DiscordSocketClient _client; private readonly Logger _log; diff --git a/src/NadekoBot/Services/Games/GamesService.cs b/src/NadekoBot/Services/Games/GamesService.cs index e677990b..d5822833 100644 --- a/src/NadekoBot/Services/Games/GamesService.cs +++ b/src/NadekoBot/Services/Games/GamesService.cs @@ -15,7 +15,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Games { - public class GamesService + public class GamesService : INService { private readonly BotConfig _bc; diff --git a/src/NadekoBot/Services/Games/PollService.cs b/src/NadekoBot/Services/Games/PollService.cs index ba6fee19..c2d77066 100644 --- a/src/NadekoBot/Services/Games/PollService.cs +++ b/src/NadekoBot/Services/Games/PollService.cs @@ -9,7 +9,7 @@ using NLog; namespace NadekoBot.Services.Games { - public class PollService : IEarlyBlockingExecutor + public class PollService : IEarlyBlockingExecutor, INService { public ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); private readonly Logger _log; diff --git a/src/NadekoBot/Services/GreetSettingsService.cs b/src/NadekoBot/Services/GreetSettingsService.cs index ac3b84da..10b0899c 100644 --- a/src/NadekoBot/Services/GreetSettingsService.cs +++ b/src/NadekoBot/Services/GreetSettingsService.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services { - public class GreetSettingsService + public class GreetSettingsService : INService { private readonly DbService _db; diff --git a/src/NadekoBot/Services/Help/HelpService.cs b/src/NadekoBot/Services/Help/HelpService.cs index cdc6adbe..b685a50b 100644 --- a/src/NadekoBot/Services/Help/HelpService.cs +++ b/src/NadekoBot/Services/Help/HelpService.cs @@ -11,7 +11,7 @@ using NadekoBot.Attributes; namespace NadekoBot.Services.Help { - public class HelpService : ILateExecutor + public class HelpService : ILateExecutor, INService { private readonly BotConfig _bc; private readonly CommandHandler _ch; diff --git a/src/NadekoBot/Services/IGoogleApiService.cs b/src/NadekoBot/Services/IGoogleApiService.cs index f2e26f11..758a7d0f 100644 --- a/src/NadekoBot/Services/IGoogleApiService.cs +++ b/src/NadekoBot/Services/IGoogleApiService.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services { - public interface IGoogleApiService + public interface IGoogleApiService : INService { IEnumerable Languages { get; } diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs index 31e6c32a..5678191c 100644 --- a/src/NadekoBot/Services/IImagesService.cs +++ b/src/NadekoBot/Services/IImagesService.cs @@ -3,7 +3,7 @@ using System.Collections.Immutable; namespace NadekoBot.Services { - public interface IImagesService + public interface IImagesService : INService { ImmutableArray Heads { get; } ImmutableArray Tails { get; } diff --git a/src/NadekoBot/Services/INService.cs b/src/NadekoBot/Services/INService.cs new file mode 100644 index 00000000..76c30abb --- /dev/null +++ b/src/NadekoBot/Services/INService.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services +{ + /// + /// All services must implement this interface in order to be auto-discovered by the DI system + /// + public interface INService + { + + } +} diff --git a/src/NadekoBot/Services/IStatsService.cs b/src/NadekoBot/Services/IStatsService.cs index d187c413..800b8fba 100644 --- a/src/NadekoBot/Services/IStatsService.cs +++ b/src/NadekoBot/Services/IStatsService.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services { - public interface IStatsService + public interface IStatsService : INService { string Author { get; } long CommandsRan { get; } diff --git a/src/NadekoBot/Services/Impl/NadekoStrings.cs b/src/NadekoBot/Services/Impl/NadekoStrings.cs index fb0d6f17..f6273770 100644 --- a/src/NadekoBot/Services/Impl/NadekoStrings.cs +++ b/src/NadekoBot/Services/Impl/NadekoStrings.cs @@ -11,7 +11,7 @@ using System.Text.RegularExpressions; namespace NadekoBot.Services { - public class NadekoStrings + public class NadekoStrings : INService { public const string stringsPath = @"_strings/"; diff --git a/src/NadekoBot/Services/Impl/SoundCloudApiService.cs b/src/NadekoBot/Services/Impl/SoundCloudApiService.cs index a3dc8cdd..acdba5b7 100644 --- a/src/NadekoBot/Services/Impl/SoundCloudApiService.cs +++ b/src/NadekoBot/Services/Impl/SoundCloudApiService.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Impl { - public class SoundCloudApiService + public class SoundCloudApiService : INService { private readonly IBotCredentials _creds; diff --git a/src/NadekoBot/Services/Impl/StartingGuildsListService.cs b/src/NadekoBot/Services/Impl/StartingGuildsListService.cs new file mode 100644 index 00000000..e7f413cc --- /dev/null +++ b/src/NadekoBot/Services/Impl/StartingGuildsListService.cs @@ -0,0 +1,24 @@ +using Discord.WebSocket; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Collections; + +namespace NadekoBot.Services.Impl +{ + public class StartingGuildsService : IEnumerable, INService + { + private readonly ImmutableList _guilds; + + public StartingGuildsService(DiscordSocketClient client) + { + this._guilds = client.Guilds.Select(x => (long)x.Id).ToImmutableList(); + } + + public IEnumerator GetEnumerator() => + _guilds.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + _guilds.GetEnumerator(); + } +} diff --git a/src/NadekoBot/Services/Music/MusicService.cs b/src/NadekoBot/Services/Music/MusicService.cs index 5d949618..4b498b42 100644 --- a/src/NadekoBot/Services/Music/MusicService.cs +++ b/src/NadekoBot/Services/Music/MusicService.cs @@ -15,7 +15,7 @@ using NadekoBot.Services.Impl; namespace NadekoBot.Services.Music { - public class MusicService + public class MusicService : INService { public const string MusicDataPath = "data/musicdata"; diff --git a/src/NadekoBot/Services/Permissions/BlacklistService.cs b/src/NadekoBot/Services/Permissions/BlacklistService.cs index 4f7a691d..cad2f356 100644 --- a/src/NadekoBot/Services/Permissions/BlacklistService.cs +++ b/src/NadekoBot/Services/Permissions/BlacklistService.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Permissions { - public class BlacklistService : IEarlyBlocker + public class BlacklistService : IEarlyBlocker, INService { public ConcurrentHashSet BlacklistedUsers { get; } public ConcurrentHashSet BlacklistedGuilds { get; } diff --git a/src/NadekoBot/Services/Permissions/CmdCdService.cs b/src/NadekoBot/Services/Permissions/CmdCdService.cs index 7a44a292..27a24ea5 100644 --- a/src/NadekoBot/Services/Permissions/CmdCdService.cs +++ b/src/NadekoBot/Services/Permissions/CmdCdService.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Permissions { - public class CmdCdService : ILateBlocker + public class CmdCdService : ILateBlocker, INService { public ConcurrentDictionary> CommandCooldowns { get; } public ConcurrentDictionary> ActiveCooldowns { get; } = new ConcurrentDictionary>(); diff --git a/src/NadekoBot/Services/Permissions/FilterService.cs b/src/NadekoBot/Services/Permissions/FilterService.cs index 9ac67d4d..38885646 100644 --- a/src/NadekoBot/Services/Permissions/FilterService.cs +++ b/src/NadekoBot/Services/Permissions/FilterService.cs @@ -12,7 +12,7 @@ using NLog; namespace NadekoBot.Services.Permissions { - public class FilterService : IEarlyBlocker + public class FilterService : IEarlyBlocker, INService { private readonly Logger _log; diff --git a/src/NadekoBot/Services/Permissions/GlobalPermissionService.cs b/src/NadekoBot/Services/Permissions/GlobalPermissionService.cs index 419f77f3..734dd9a8 100644 --- a/src/NadekoBot/Services/Permissions/GlobalPermissionService.cs +++ b/src/NadekoBot/Services/Permissions/GlobalPermissionService.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Permissions { - public class GlobalPermissionService : ILateBlocker + public class GlobalPermissionService : ILateBlocker, INService { public readonly ConcurrentHashSet BlockedModules; public readonly ConcurrentHashSet BlockedCommands; diff --git a/src/NadekoBot/Services/Permissions/PermissionsService.cs b/src/NadekoBot/Services/Permissions/PermissionsService.cs index c4c586a9..597ab2ba 100644 --- a/src/NadekoBot/Services/Permissions/PermissionsService.cs +++ b/src/NadekoBot/Services/Permissions/PermissionsService.cs @@ -11,14 +11,14 @@ using System.Threading.Tasks; using Discord; using Discord.WebSocket; using NadekoBot.Extensions; +using NadekoBot.Services.Database; using NadekoBot.Services; namespace NadekoBot.Services.Permissions { - public class PermissionService : ILateBlocker + public class PermissionService : ILateBlocker, INService { private readonly DbService _db; - private readonly Logger _log; private readonly CommandHandler _cmd; private readonly NadekoStrings _strings; @@ -26,16 +26,15 @@ namespace NadekoBot.Services.Permissions public ConcurrentDictionary Cache { get; } = new ConcurrentDictionary(); - public PermissionService(DiscordSocketClient client, DbService db, BotConfig bc, CommandHandler cmd, NadekoStrings strings) + public PermissionService(DiscordSocketClient client, DbService db, CommandHandler cmd, NadekoStrings strings) { - _log = LogManager.GetCurrentClassLogger(); _db = db; _cmd = cmd; _strings = strings; var sw = Stopwatch.StartNew(); if (client.ShardId == 0) - TryMigratePermissions(bc); + TryMigratePermissions(); using (var uow = _db.UnitOfWork) { @@ -49,9 +48,6 @@ namespace NadekoBot.Services.Permissions }); } } - - sw.Stop(); - _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); } public PermissionCache GetCache(ulong guildId) @@ -71,12 +67,13 @@ namespace NadekoBot.Services.Permissions return pc; } - private void TryMigratePermissions(BotConfig bc) + private void TryMigratePermissions() { - var log = LogManager.GetCurrentClassLogger(); - if (bc.PermissionVersion <= 1) + using (var uow = _db.UnitOfWork) { - using (var uow = _db.UnitOfWork) + var bc = uow.BotConfig.GetOrCreate(); + var log = LogManager.GetCurrentClassLogger(); + if (bc.PermissionVersion <= 1) { log.Info("Permission version is 1, upgrading to 2."); var oldCache = new ConcurrentDictionary(uow.GuildConfigs @@ -134,25 +131,22 @@ namespace NadekoBot.Services.Permissions bc.PermissionVersion = 2; uow.Complete(); } - } - if (bc.PermissionVersion <= 2) - { - using (var uow = _db.UnitOfWork) + if (bc.PermissionVersion <= 2) { var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" }; uow._context.Database.ExecuteSqlCommand( -$@"UPDATE {nameof(Permissionv2)} + $@"UPDATE {nameof(Permissionv2)} SET secondaryTargetName=trim(substr(secondaryTargetName, 3)) WHERE secondaryTargetName LIKE '!!%' OR secondaryTargetName LIKE '!m%'; UPDATE {nameof(Permissionv2)} SET secondaryTargetName=substr(secondaryTargetName, 2) WHERE secondaryTargetName LIKE '.%' OR - secondaryTargetName LIKE '~%' OR - secondaryTargetName LIKE ';%' OR - secondaryTargetName LIKE '>%' OR - secondaryTargetName LIKE '-%' OR - secondaryTargetName LIKE '!%';"); +secondaryTargetName LIKE '~%' OR +secondaryTargetName LIKE ';%' OR +secondaryTargetName LIKE '>%' OR +secondaryTargetName LIKE '-%' OR +secondaryTargetName LIKE '!%';"); bc.PermissionVersion = 3; uow.Complete(); } @@ -229,4 +223,4 @@ WHERE secondaryTargetName LIKE '.%' OR return false; } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Pokemon/PokemonService.cs b/src/NadekoBot/Services/Pokemon/PokemonService.cs index d2160865..9ac73bf1 100644 --- a/src/NadekoBot/Services/Pokemon/PokemonService.cs +++ b/src/NadekoBot/Services/Pokemon/PokemonService.cs @@ -6,7 +6,7 @@ using System.IO; namespace NadekoBot.Services.Pokemon { - public class PokemonService + public class PokemonService : INService { public readonly List PokemonTypes = new List(); public readonly ConcurrentDictionary Stats = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Services/Searches/AnimeSearchService.cs b/src/NadekoBot/Services/Searches/AnimeSearchService.cs index a4187da3..bcf9a3a3 100644 --- a/src/NadekoBot/Services/Searches/AnimeSearchService.cs +++ b/src/NadekoBot/Services/Searches/AnimeSearchService.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Searches { - public class AnimeSearchService + public class AnimeSearchService : INService { private readonly Logger _log; diff --git a/src/NadekoBot/Services/Searches/SearchesService.cs b/src/NadekoBot/Services/Searches/SearchesService.cs index 54a268c8..43cc2a52 100644 --- a/src/NadekoBot/Services/Searches/SearchesService.cs +++ b/src/NadekoBot/Services/Searches/SearchesService.cs @@ -15,7 +15,7 @@ using System.Xml; namespace NadekoBot.Services.Searches { - public class SearchesService + public class SearchesService : INService { private readonly DiscordSocketClient _client; private readonly IGoogleApiService _google; diff --git a/src/NadekoBot/Services/Searches/StreamNotificationService.cs b/src/NadekoBot/Services/Searches/StreamNotificationService.cs index c077e4e1..3b4039a1 100644 --- a/src/NadekoBot/Services/Searches/StreamNotificationService.cs +++ b/src/NadekoBot/Services/Searches/StreamNotificationService.cs @@ -15,7 +15,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Searches { - public class StreamNotificationService + public class StreamNotificationService : INService { private readonly Timer _streamCheckTimer; private bool firstStreamNotifPass { get; set; } = true; diff --git a/src/NadekoBot/Services/ServiceProvider.cs b/src/NadekoBot/Services/ServiceProvider.cs index c3b46ba3..03eebec6 100644 --- a/src/NadekoBot/Services/ServiceProvider.cs +++ b/src/NadekoBot/Services/ServiceProvider.cs @@ -3,6 +3,15 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; +using System.Reflection; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Services.Database.Models; +using NadekoBot.Services.Impl; +using System.Linq; +using NadekoBot.Extensions; +using System.Diagnostics; +using NLog; namespace NadekoBot.Services { @@ -16,8 +25,14 @@ namespace NadekoBot.Services public class ServiceProviderBuilder { private ConcurrentDictionary _dict = new ConcurrentDictionary(); + private readonly Logger _log; - public ServiceProviderBuilder Add(T obj) + public ServiceProviderBuilder() + { + _log = LogManager.GetCurrentClassLogger(); + } + + public ServiceProviderBuilder AddManual(T obj) { _dict.TryAdd(typeof(T), obj); return this; @@ -27,6 +42,61 @@ namespace NadekoBot.Services { return new NServiceProvider(_dict); } + + public ServiceProviderBuilder LoadFrom(Assembly assembly) + { + var allTypes = assembly.GetTypes(); + var services = new Queue(allTypes + .Where(x => x.GetInterfaces().Contains(typeof(INService)) && !x.GetTypeInfo().IsInterface && !x.GetTypeInfo().IsAbstract) + .ToArray()); + + var interfaces = new HashSet(allTypes + .Where(x => x.GetInterfaces().Contains(typeof(INService)) && x.GetTypeInfo().IsInterface)); + + var sw = Stopwatch.StartNew(); + var swInstance = new Stopwatch(); + while (services.Count > 0) + { + var type = services.Dequeue(); //get a type i need to make an instance of + + if (_dict.TryGetValue(type, out _)) // if that type is already instantiated, skip + continue; + + var ctor = type.GetConstructors()[0]; + var argTypes = ctor + .GetParameters() + .Select(x => x.ParameterType) + .ToArray(); // get constructor argument types i need to pass in + + var args = new List(argTypes.Length); + foreach (var arg in argTypes) //get constructor arguments from the dictionary of already instantiated types + { + if (_dict.TryGetValue(arg, out var argObj)) //if i got current one, add it to the list of instances and move on + args.Add(argObj); + else //if i failed getting it, add it to the end, and break + { + services.Enqueue(type); + break; + } + } + if (args.Count != argTypes.Length) + continue; + swInstance.Restart(); + var instance = ctor.Invoke(args.ToArray()); + swInstance.Stop(); + if (swInstance.Elapsed.TotalSeconds > 5) + _log.Info($"{type.Name} took {swInstance.Elapsed.TotalSeconds:F2}s to load."); + var interfaceType = interfaces.FirstOrDefault(x => instance.GetType().GetInterfaces().Contains(x)); + if (interfaceType != null) + _dict.TryAdd(interfaceType, instance); + + _dict.TryAdd(type, instance); + } + sw.Stop(); + _log.Info($"All services loaded in {sw.Elapsed.TotalSeconds:F2}s"); + + return this; + } } private readonly ImmutableDictionary _services; diff --git a/src/NadekoBot/Services/Utility/CommandMapService.cs b/src/NadekoBot/Services/Utility/CommandMapService.cs index 9fe6e546..5c07688e 100644 --- a/src/NadekoBot/Services/Utility/CommandMapService.cs +++ b/src/NadekoBot/Services/Utility/CommandMapService.cs @@ -10,7 +10,7 @@ using NadekoBot.Extensions; namespace NadekoBot.Services.Utility { - public class CommandMapService : IInputTransformer + public class CommandMapService : IInputTransformer, INService { private readonly Logger _log; diff --git a/src/NadekoBot/Services/Utility/ConverterService.cs b/src/NadekoBot/Services/Utility/ConverterService.cs index 45334b48..9d4dfcff 100644 --- a/src/NadekoBot/Services/Utility/ConverterService.cs +++ b/src/NadekoBot/Services/Utility/ConverterService.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Utility { - public class ConverterService + public class ConverterService : INService { public List Units { get; } = new List(); private readonly Logger _log; diff --git a/src/NadekoBot/Services/Utility/MessageRepeaterService.cs b/src/NadekoBot/Services/Utility/MessageRepeaterService.cs index cc402aa8..eeaecf37 100644 --- a/src/NadekoBot/Services/Utility/MessageRepeaterService.cs +++ b/src/NadekoBot/Services/Utility/MessageRepeaterService.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Utility { //todo 50 rewrite - public class MessageRepeaterService + public class MessageRepeaterService : INService { //messagerepeater //guildid/RepeatRunners @@ -19,8 +19,7 @@ namespace NadekoBot.Services.Utility { var _ = Task.Run(async () => { - while (!bot.Ready) - await Task.Delay(1000); + await bot.Ready.Task.ConfigureAwait(false); Repeaters = new ConcurrentDictionary>(gcs .ToDictionary(gc => gc.GuildId, diff --git a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs index 7c08f747..b7fae1f4 100644 --- a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs +++ b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs @@ -9,13 +9,12 @@ using System.Collections.Immutable; using System.IO; using System.Linq; using System.Net.Http; -using System.Text; using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Services.Utility { - public class PatreonRewardsService + public class PatreonRewardsService : INService { private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1); diff --git a/src/NadekoBot/Services/Utility/RemindService.cs b/src/NadekoBot/Services/Utility/RemindService.cs index 48f3f869..89bc20a6 100644 --- a/src/NadekoBot/Services/Utility/RemindService.cs +++ b/src/NadekoBot/Services/Utility/RemindService.cs @@ -4,9 +4,9 @@ using NadekoBot.DataStructures.Replacements; using NadekoBot.Extensions; using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; +using NadekoBot.Services.Impl; using NLog; using System; -using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading; @@ -14,7 +14,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Utility { - public class RemindService + public class RemindService : INService { public readonly Regex Regex = new Regex(@"^(?:(?\d)mo)?(?:(?\d)w)?(?:(?\d{1,2})d)?(?:(?\d{1,2})h)?(?:(?\d{1,2})m)?$", RegexOptions.Compiled | RegexOptions.Multiline); @@ -28,8 +28,8 @@ namespace NadekoBot.Services.Utility private readonly DiscordSocketClient _client; private readonly DbService _db; - public RemindService(DiscordSocketClient client, BotConfig config, DbService db, - List guilds, IUnitOfWork uow) + public RemindService(DiscordSocketClient client, BotConfig config, DbService db, + StartingGuildsService guilds, IUnitOfWork uow) { _config = config; _client = client; diff --git a/src/NadekoBot/Services/Utility/StreamRoleService.cs b/src/NadekoBot/Services/Utility/StreamRoleService.cs index 2bd8a156..25863135 100644 --- a/src/NadekoBot/Services/Utility/StreamRoleService.cs +++ b/src/NadekoBot/Services/Utility/StreamRoleService.cs @@ -12,7 +12,7 @@ using NLog; namespace NadekoBot.Services.Utility { - public class StreamRoleService + public class StreamRoleService : INService { private readonly DbService _db; private readonly ConcurrentDictionary guildSettings; @@ -26,6 +26,7 @@ namespace NadekoBot.Services.Utility this._log = LogManager.GetCurrentClassLogger(); guildSettings = gcs.ToDictionary(x => x.GuildId, x => x.StreamRole) + .Where(x => x.Value.FromRoleId != 0 && x.Value.AddRoleId != 0) .ToConcurrent(); client.GuildMemberUpdated += Client_GuildMemberUpdated; diff --git a/src/NadekoBot/Services/Utility/VerboseErrorsService.cs b/src/NadekoBot/Services/Utility/VerboseErrorsService.cs index 7658ea1c..8ed89d46 100644 --- a/src/NadekoBot/Services/Utility/VerboseErrorsService.cs +++ b/src/NadekoBot/Services/Utility/VerboseErrorsService.cs @@ -10,7 +10,7 @@ using System.Linq; namespace NadekoBot.Services.Utility { - public class VerboseErrorsService + public class VerboseErrorsService : INService { private readonly ConcurrentHashSet guildsEnabled; private readonly DbService _db; diff --git a/src/NadekoBot/ShardsCoordinator.cs b/src/NadekoBot/ShardsCoordinator.cs index 6846e4d2..9e4851bd 100644 --- a/src/NadekoBot/ShardsCoordinator.cs +++ b/src/NadekoBot/ShardsCoordinator.cs @@ -3,7 +3,6 @@ using NadekoBot.Services; using NadekoBot.Services.Impl; using NLog; using System; -using System.Collections.Concurrent; using System.Diagnostics; using System.Linq; using System.Threading.Tasks;