diff --git a/src/NadekoBot/Bot.cs b/src/NadekoBot/Bot.cs index 44dcabe8e..6f3f12df7 100644 --- a/src/NadekoBot/Bot.cs +++ b/src/NadekoBot/Bot.cs @@ -28,32 +28,29 @@ using Serilog; namespace NadekoBot { - // todo remove all migration code from - // todo read prev commit - public class Bot + public sealed class Bot { private readonly IBotCredentials _creds; - public DiscordSocketClient Client { get; } - public CommandService CommandService { get; } - + private readonly CommandService _commandService; private readonly DbService _db; + private readonly BotCredsProvider _credsProvider; + + public event Func JoinedGuild = delegate { return Task.CompletedTask; }; + + public DiscordSocketClient Client { get; } public ImmutableArray AllGuildConfigs { get; private set; } + // todo change configs to records // todo remove colors from here public static Color OkColor { get; set; } public static Color ErrorColor { get; set; } public static Color PendingColor { get; set; } - // todo remove ready prop - public TaskCompletionSource Ready { get; private set; } = new TaskCompletionSource(); - - public IServiceProvider Services { get; private set; } + private IServiceProvider Services { get; set; } - public string Mention { get; set; } + public string Mention { get; private set; } + public bool IsReady { get; private set; } - public event Func JoinedGuild = delegate { return Task.CompletedTask; }; - - private readonly BotCredsProvider _credsProvider; public Bot(int shardId, int? totalShards) { if (shardId < 0) @@ -80,7 +77,7 @@ namespace NadekoBot ExclusiveBulkDelete = true, }); - CommandService = new CommandService(new CommandServiceConfig() + _commandService = new CommandService(new CommandServiceConfig() { CaseSensitiveCommands = false, DefaultRunMode = RunMode.Sync, @@ -96,12 +93,6 @@ namespace NadekoBot return Client.Guilds.Select(x => x.Id).ToList(); } - public IEnumerable GetCurrentGuildConfigs() - { - using var uow = _db.GetDbContext(); - return uow.GuildConfigs.GetAllGuildConfigs(GetCurrentGuildIds()).ToImmutableArray(); - } - private void AddServices() { var startingGuildIdList = GetCurrentGuildIds(); @@ -119,7 +110,7 @@ namespace NadekoBot .AddSingleton(_db) // database .AddRedis(_creds.RedisOptions) // redis .AddSingleton(Client) // discord socket client - .AddSingleton(CommandService) + .AddSingleton(_commandService) .AddSingleton(this) // pepega .AddSingleton() .AddSingleton() @@ -130,6 +121,7 @@ namespace NadekoBot .AddConfigMigrators() // todo remove config migrators .AddMemoryCache() .AddSingleton() + .AddSingleton() // music .AddMusic() ; @@ -152,9 +144,27 @@ namespace NadekoBot .AddSingleton(x => (IReadyExecutor)x.GetRequiredService()); } - svcs.AddSingleton(x => x.GetService()); - svcs.AddSingleton(x => x.GetService()); - svcs.AddSingleton(x => x.GetService()); + svcs.Scan(scan => scan + .FromAssemblyOf() + .AddClasses(classes => classes.AssignableTo()) + .AsSelf() + .AsImplementedInterfaces() + .WithSingletonLifetime() + + // behaviours + .AddClasses(classes => classes.AssignableToAny( + typeof(IEarlyBehavior), + typeof(ILateBlocker), + typeof(IInputTransformer), + typeof(ILateExecutor))) + .AsSelf() + .AsImplementedInterfaces() + .WithSingletonLifetime() + ); + + // svcs.AddSingleton(x => x.GetService()); + // svcs.AddSingleton(x => x.GetService()); + // svcs.AddSingleton(x => x.GetService()); //initialize Services Services = svcs.BuildServiceProvider(); @@ -164,9 +174,7 @@ namespace NadekoBot { ApplyConfigMigrations(); } - - //what the fluff - commandHandler.AddServices(svcs); + _ = LoadTypeReaders(typeof(Bot).Assembly); sw.Stop(); @@ -204,10 +212,10 @@ namespace NadekoBot var toReturn = new List(); foreach (var ft in filteredTypes) { - var x = (TypeReader)Activator.CreateInstance(ft, Client, CommandService); + var x = (TypeReader)Activator.CreateInstance(ft, Client, _commandService); var baseType = ft.BaseType; var typeArgs = baseType.GetGenericArguments(); - CommandService.AddTypeReader(typeArgs[0], x); + _commandService.AddTypeReader(typeArgs[0], x); toReturn.Add(x); } @@ -234,10 +242,6 @@ namespace NadekoBot { // ignored } - finally - { - - } }); return Task.CompletedTask; } @@ -325,7 +329,7 @@ namespace NadekoBot .ConfigureAwait(false); HandleStatusChanges(); - Ready.TrySetResult(true); + IsReady = true; _ = Task.Run(ExecuteReadySubscriptions); Log.Information("Shard {ShardId} ready", Client.ShardId); } diff --git a/src/NadekoBot/Common/ModuleBehaviors/IEarlyBlocker.cs b/src/NadekoBot/Common/ModuleBehaviors/IEarlyBlocker.cs index ac7909ed1..5f2b18c4b 100644 --- a/src/NadekoBot/Common/ModuleBehaviors/IEarlyBlocker.cs +++ b/src/NadekoBot/Common/ModuleBehaviors/IEarlyBlocker.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Discord; -using Discord.WebSocket; namespace NadekoBot.Common.ModuleBehaviors { @@ -12,7 +11,7 @@ namespace NadekoBot.Common.ModuleBehaviors int Priority { get; } ModuleBehaviorType BehaviorType { get; } - Task RunBehavior(DiscordSocketClient client, IGuild guild, IUserMessage msg); + Task RunBehavior(IGuild guild, IUserMessage msg); } public enum ModuleBehaviorType diff --git a/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs b/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs index 049e50fa6..2a4a634ee 100644 --- a/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs +++ b/src/NadekoBot/Common/ModuleBehaviors/ILateBlocker.cs @@ -8,7 +8,6 @@ namespace NadekoBot.Common.ModuleBehaviors { public int Priority { get; } - Task TryBlockLate(DiscordSocketClient client, ICommandContext context, - string moduleName, CommandInfo command); + Task TryBlockLate(ICommandContext context, string moduleName, CommandInfo command); } } diff --git a/src/NadekoBot/Common/ModuleBehaviors/ILateExecutor.cs b/src/NadekoBot/Common/ModuleBehaviors/ILateExecutor.cs index 53e878e47..66aff2477 100644 --- a/src/NadekoBot/Common/ModuleBehaviors/ILateExecutor.cs +++ b/src/NadekoBot/Common/ModuleBehaviors/ILateExecutor.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Discord; -using Discord.WebSocket; namespace NadekoBot.Common.ModuleBehaviors { @@ -9,6 +8,6 @@ namespace NadekoBot.Common.ModuleBehaviors /// public interface ILateExecutor { - Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg); + Task LateExecute(IGuild guild, IUserMessage msg); } } diff --git a/src/NadekoBot/Modules/Administration/Services/DiscordPermOverrideService.cs b/src/NadekoBot/Modules/Administration/Services/DiscordPermOverrideService.cs index fe6528973..0429a1264 100644 --- a/src/NadekoBot/Modules/Administration/Services/DiscordPermOverrideService.cs +++ b/src/NadekoBot/Modules/Administration/Services/DiscordPermOverrideService.cs @@ -142,8 +142,7 @@ namespace NadekoBot.Modules.Administration.Services } } - public async Task TryBlockLate(DiscordSocketClient client, ICommandContext context, string moduleName, - CommandInfo command) + public async Task TryBlockLate(ICommandContext context, string moduleName, CommandInfo command) { if (TryGetOverrides(context.Guild?.Id ?? 0, command.Name, out var perm) && perm is not null) { diff --git a/src/NadekoBot/Modules/Administration/Services/SelfService.cs b/src/NadekoBot/Modules/Administration/Services/SelfService.cs index a506518b0..0ea05e644 100644 --- a/src/NadekoBot/Modules/Administration/Services/SelfService.cs +++ b/src/NadekoBot/Modules/Administration/Services/SelfService.cs @@ -18,7 +18,7 @@ using Serilog; namespace NadekoBot.Modules.Administration.Services { - public sealed class SelfService : ILateExecutor, IReadyExecutor, INService + public sealed class SelfService : ILateExecutor, IReadyExecutor { private readonly ConnectionMultiplexer _redis; private readonly CommandHandler _cmdHandler; @@ -54,6 +54,7 @@ namespace NadekoBot.Modules.Administration.Services _httpFactory = factory; _bss = bss; + Log.Information("Self service created"); var sub = _redis.GetSubscriber(); if (_client.ShardId == 0) { @@ -226,7 +227,7 @@ namespace NadekoBot.Modules.Administration.Services } // forwards dms - public async Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg) + public async Task LateExecute(IGuild guild, IUserMessage msg) { var bs = _bss.Data; if (msg.Channel is IDMChannel && _bss.Data.ForwardMessages && ownerChannels.Any()) diff --git a/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs b/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs index f980438b5..0f6266085 100644 --- a/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs +++ b/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs @@ -22,7 +22,7 @@ using YamlDotNet.Serialization; namespace NadekoBot.Modules.CustomReactions.Services { - public sealed class CustomReactionsService : IEarlyBehavior, INService, IReadyExecutor + public sealed class CustomReactionsService : IEarlyBehavior, IReadyExecutor { public enum CrField { @@ -77,6 +77,7 @@ namespace NadekoBot.Modules.CustomReactions.Services _pubSub = pubSub; _rng = new NadekoRandom(); + Log.Information("Custom reaction service created"); _pubSub.Sub(_crsReloadedKey, OnCrsShouldReload); pubSub.Sub(_gcrAddedKey, OnGcrAdded); pubSub.Sub(_gcrDeletedkey, OnGcrDeleted); @@ -380,7 +381,7 @@ namespace NadekoBot.Modules.CustomReactions.Services return result[_rng.Next(0, result.Count)]; } - public async Task RunBehavior(DiscordSocketClient client, IGuild guild, IUserMessage msg) + public async Task RunBehavior(IGuild guild, IUserMessage msg) { // maybe this message is a custom reaction var cr = TryGetCustomReaction(msg); diff --git a/src/NadekoBot/Modules/Gambling/Services/AnimalRaceService.cs b/src/NadekoBot/Modules/Gambling/Services/AnimalRaceService.cs index 53776b788..8d3c6c520 100644 --- a/src/NadekoBot/Modules/Gambling/Services/AnimalRaceService.cs +++ b/src/NadekoBot/Modules/Gambling/Services/AnimalRaceService.cs @@ -5,17 +5,8 @@ using NadekoBot.Modules.Gambling.Common.AnimalRacing; namespace NadekoBot.Modules.Gambling.Services { - public class AnimalRaceService : INService, IUnloadableService + public class AnimalRaceService : INService { public ConcurrentDictionary AnimalRaces { get; } = new ConcurrentDictionary(); - - public Task Unload() - { - foreach (var kvp in AnimalRaces) - { - try { kvp.Value.Dispose(); } catch { } - } - return Task.CompletedTask; - } } } diff --git a/src/NadekoBot/Modules/Games/Services/ChatterbotService.cs b/src/NadekoBot/Modules/Games/Services/ChatterbotService.cs index 79a473863..ad5245c24 100644 --- a/src/NadekoBot/Modules/Games/Services/ChatterbotService.cs +++ b/src/NadekoBot/Modules/Games/Services/ChatterbotService.cs @@ -15,7 +15,7 @@ using Serilog; namespace NadekoBot.Modules.Games.Services { - public class ChatterBotService : IEarlyBehavior, INService + public class ChatterBotService : IEarlyBehavior { private readonly DiscordSocketClient _client; private readonly PermissionService _perms; @@ -103,7 +103,7 @@ namespace NadekoBot.Modules.Games.Services return true; } - public async Task RunBehavior(DiscordSocketClient client, IGuild guild, IUserMessage usrMsg) + public async Task RunBehavior(IGuild guild, IUserMessage usrMsg) { if (!(guild is SocketGuild sg)) return false; diff --git a/src/NadekoBot/Modules/Games/Services/GamesService.cs b/src/NadekoBot/Modules/Games/Services/GamesService.cs index 51898a2ee..939af0f03 100644 --- a/src/NadekoBot/Modules/Games/Services/GamesService.cs +++ b/src/NadekoBot/Modules/Games/Services/GamesService.cs @@ -21,7 +21,7 @@ using Serilog; namespace NadekoBot.Modules.Games.Services { - public class GamesService : INService, IUnloadableService + public class GamesService : INService { private readonly GamesConfigService _gamesConfig; @@ -101,26 +101,6 @@ namespace NadekoBot.Modules.Games.Services } } - public async Task Unload() - { - _t.Change(Timeout.Infinite, Timeout.Infinite); - - AcrophobiaGames.ForEach(x => x.Value.Dispose()); - AcrophobiaGames.Clear(); - HangmanGames.ForEach(x => x.Value.Dispose()); - HangmanGames.Clear(); - await Task.WhenAll(RunningTrivias.Select(x => x.Value.StopGame())).ConfigureAwait(false); - RunningTrivias.Clear(); - - TicTacToeGames.Clear(); - - await Task.WhenAll(RunningContests.Select(x => x.Value.Stop())) - .ConfigureAwait(false); - RunningContests.Clear(); - NunchiGames.ForEach(x => x.Value.Dispose()); - NunchiGames.Clear(); - } - public void AddTypingArticle(IUser user, string text) { TypingArticles.Add(new TypingArticle diff --git a/src/NadekoBot/Modules/Games/Services/PollService.cs b/src/NadekoBot/Modules/Games/Services/PollService.cs index faddfc022..f19d1e93f 100644 --- a/src/NadekoBot/Modules/Games/Services/PollService.cs +++ b/src/NadekoBot/Modules/Games/Services/PollService.cs @@ -3,7 +3,6 @@ using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; using Discord; -using Discord.WebSocket; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Modules.Games.Common; using NadekoBot.Db.Models; @@ -16,7 +15,7 @@ using Serilog; namespace NadekoBot.Modules.Games.Services { - public class PollService : IEarlyBehavior, INService + public class PollService : IEarlyBehavior { public ConcurrentDictionary ActivePolls { get; } = new ConcurrentDictionary(); @@ -106,7 +105,7 @@ namespace NadekoBot.Modules.Games.Services try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } } - public async Task RunBehavior(DiscordSocketClient client, IGuild guild, IUserMessage msg) + public async Task RunBehavior(IGuild guild, IUserMessage msg) { if (guild is null) return false; diff --git a/src/NadekoBot/Modules/Help/Services/HelpService.cs b/src/NadekoBot/Modules/Help/Services/HelpService.cs index acee9f2d9..530dcc11a 100644 --- a/src/NadekoBot/Modules/Help/Services/HelpService.cs +++ b/src/NadekoBot/Modules/Help/Services/HelpService.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Discord; -using Discord.WebSocket; using System; using Discord.Commands; using NadekoBot.Extensions; @@ -40,7 +39,7 @@ namespace NadekoBot.Modules.Help.Services .Build(); } - public Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg) + public Task LateExecute(IGuild guild, IUserMessage msg) { var settings = _bss.Data; if (guild is null) diff --git a/src/NadekoBot/Modules/Permissions/Services/BlacklistService.cs b/src/NadekoBot/Modules/Permissions/Services/BlacklistService.cs index 955ba3f75..b2ffc1c1d 100644 --- a/src/NadekoBot/Modules/Permissions/Services/BlacklistService.cs +++ b/src/NadekoBot/Modules/Permissions/Services/BlacklistService.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Discord; -using Discord.WebSocket; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Services; using NadekoBot.Services.Database.Models; @@ -12,7 +11,7 @@ using NadekoBot.Db; namespace NadekoBot.Modules.Permissions.Services { - public sealed class BlacklistService : IEarlyBehavior, INService + public sealed class BlacklistService : IEarlyBehavior { private readonly DbService _db; private readonly IPubSub _pubSub; @@ -39,7 +38,7 @@ namespace NadekoBot.Modules.Permissions.Services return default; } - public Task RunBehavior(DiscordSocketClient _, IGuild guild, IUserMessage usrMsg) + public Task RunBehavior(IGuild guild, IUserMessage usrMsg) { foreach (var bl in _blacklist) { diff --git a/src/NadekoBot/Modules/Permissions/Services/CmdCdService.cs b/src/NadekoBot/Modules/Permissions/Services/CmdCdService.cs index 687e5fba2..e6fc44b80 100644 --- a/src/NadekoBot/Modules/Permissions/Services/CmdCdService.cs +++ b/src/NadekoBot/Modules/Permissions/Services/CmdCdService.cs @@ -63,7 +63,7 @@ namespace NadekoBot.Modules.Permissions.Services return Task.FromResult(false); } - public Task TryBlockLate(DiscordSocketClient client, ICommandContext ctx, string moduleName, CommandInfo command) + public Task TryBlockLate(ICommandContext ctx, string moduleName, CommandInfo command) { var guild = ctx.Guild; var user = ctx.User; diff --git a/src/NadekoBot/Modules/Permissions/Services/FilterService.cs b/src/NadekoBot/Modules/Permissions/Services/FilterService.cs index 38b701877..a72f7869a 100644 --- a/src/NadekoBot/Modules/Permissions/Services/FilterService.cs +++ b/src/NadekoBot/Modules/Permissions/Services/FilterService.cs @@ -17,7 +17,7 @@ using Serilog; namespace NadekoBot.Modules.Permissions.Services { - public class FilterService : IEarlyBehavior, INService + public class FilterService : IEarlyBehavior { private readonly DbService _db; @@ -117,13 +117,13 @@ namespace NadekoBot.Modules.Permissions.Services if (guild is null || usrMsg is null) return Task.CompletedTask; - return RunBehavior(null, guild, usrMsg); + return RunBehavior(guild, usrMsg); }); return Task.CompletedTask; }; } - public async Task RunBehavior(DiscordSocketClient _, IGuild guild, IUserMessage msg) + public async Task RunBehavior(IGuild guild, IUserMessage msg) { if (!(msg.Author is IGuildUser gu) || gu.GuildPermissions.Administrator) return false; diff --git a/src/NadekoBot/Modules/Permissions/Services/GlobalPermissionService.cs b/src/NadekoBot/Modules/Permissions/Services/GlobalPermissionService.cs index 5eed11aee..838e7635d 100644 --- a/src/NadekoBot/Modules/Permissions/Services/GlobalPermissionService.cs +++ b/src/NadekoBot/Modules/Permissions/Services/GlobalPermissionService.cs @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Permissions.Services } - public Task TryBlockLate(DiscordSocketClient client, ICommandContext ctx, string moduleName, CommandInfo command) + public Task TryBlockLate(ICommandContext ctx, string moduleName, CommandInfo command) { var settings = _bss.Data; var commandName = command.Name.ToLowerInvariant(); diff --git a/src/NadekoBot/Modules/Permissions/Services/PermissionsService.cs b/src/NadekoBot/Modules/Permissions/Services/PermissionsService.cs index 3abc01119..0f9c44fcb 100644 --- a/src/NadekoBot/Modules/Permissions/Services/PermissionsService.cs +++ b/src/NadekoBot/Modules/Permissions/Services/PermissionsService.cs @@ -98,8 +98,7 @@ namespace NadekoBot.Modules.Permissions.Services }); } - public async Task TryBlockLate(DiscordSocketClient client, ICommandContext ctx, string moduleName, - CommandInfo command) + public async Task TryBlockLate(ICommandContext ctx, string moduleName, CommandInfo command) { var guild = ctx.Guild; var msg = ctx.Message; diff --git a/src/NadekoBot/Modules/Searches/Services/SearchesService.cs b/src/NadekoBot/Modules/Searches/Services/SearchesService.cs index ddedd7729..3cf9ccf69 100644 --- a/src/NadekoBot/Modules/Searches/Services/SearchesService.cs +++ b/src/NadekoBot/Modules/Searches/Services/SearchesService.cs @@ -31,7 +31,7 @@ using Image = SixLabors.ImageSharp.Image; namespace NadekoBot.Modules.Searches.Services { - public class SearchesService : INService, IUnloadableService + public class SearchesService : INService { private readonly IHttpClientFactory _httpFactory; private readonly DiscordSocketClient _client; @@ -482,19 +482,6 @@ namespace NadekoBot.Modules.Searches.Services } } - public Task Unload() - { - AutoBoobTimers.ForEach(x => x.Value.Change(Timeout.Infinite, Timeout.Infinite)); - AutoBoobTimers.Clear(); - AutoButtTimers.ForEach(x => x.Value.Change(Timeout.Infinite, Timeout.Infinite)); - AutoButtTimers.Clear(); - AutoHentaiTimers.ForEach(x => x.Value.Change(Timeout.Infinite, Timeout.Infinite)); - AutoHentaiTimers.Clear(); - - _imageCacher.Clear(); - return Task.CompletedTask; - } - public async Task GetMtgCardAsync(string search) { search = search.Trim().ToLowerInvariant(); diff --git a/src/NadekoBot/Modules/Utility/Services/ConverterService.cs b/src/NadekoBot/Modules/Utility/Services/ConverterService.cs index eccf98771..84c4ea73d 100644 --- a/src/NadekoBot/Modules/Utility/Services/ConverterService.cs +++ b/src/NadekoBot/Modules/Utility/Services/ConverterService.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace NadekoBot.Modules.Utility.Services { - public class ConverterService : INService, IUnloadableService + public class ConverterService : INService { public ConvertUnit[] Units => _cache.Redis.GetDatabase() @@ -87,12 +87,6 @@ namespace NadekoBot.Modules.Utility.Services // ignored } } - - public Task Unload() - { - _currencyUpdater?.Change(Timeout.Infinite, Timeout.Infinite); - return Task.CompletedTask; - } } public class Rates diff --git a/src/NadekoBot/Modules/Utility/Services/PatreonRewardsService.cs b/src/NadekoBot/Modules/Utility/Services/PatreonRewardsService.cs index 48c52e66f..0d81cbbb0 100644 --- a/src/NadekoBot/Modules/Utility/Services/PatreonRewardsService.cs +++ b/src/NadekoBot/Modules/Utility/Services/PatreonRewardsService.cs @@ -16,7 +16,7 @@ using Serilog; namespace NadekoBot.Modules.Utility.Services { - public class PatreonRewardsService : INService, IUnloadableService + public class PatreonRewardsService : INService { private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1); @@ -218,11 +218,5 @@ namespace NadekoBot.Modules.Utility.Services // ignored } } - - public Task Unload() - { - _updater?.Change(Timeout.Infinite, Timeout.Infinite); - return Task.CompletedTask; - } } } diff --git a/src/NadekoBot/Modules/Utility/Services/RepeaterService.cs b/src/NadekoBot/Modules/Utility/Services/RepeaterService.cs index 6e528f81a..c915b4d39 100644 --- a/src/NadekoBot/Modules/Utility/Services/RepeaterService.cs +++ b/src/NadekoBot/Modules/Utility/Services/RepeaterService.cs @@ -20,7 +20,7 @@ using Serilog; namespace NadekoBot.Modules.Utility.Services { - public sealed class RepeaterService : IReadyExecutor, INService + public sealed class RepeaterService : IReadyExecutor { public const int MAX_REPEATERS = 5; diff --git a/src/NadekoBot/Modules/Utility/Services/StreamRoleService.cs b/src/NadekoBot/Modules/Utility/Services/StreamRoleService.cs index dd426fdd5..5a10c0897 100644 --- a/src/NadekoBot/Modules/Utility/Services/StreamRoleService.cs +++ b/src/NadekoBot/Modules/Utility/Services/StreamRoleService.cs @@ -17,7 +17,7 @@ using Serilog; namespace NadekoBot.Modules.Utility.Services { - public class StreamRoleService : INService, IUnloadableService + public class StreamRoleService : INService { private readonly DbService _db; private readonly DiscordSocketClient _client; @@ -48,12 +48,6 @@ namespace NadekoBot.Modules.Utility.Services }); } - public Task Unload() - { - _client.GuildMemberUpdated -= Client_GuildMemberUpdated; - return Task.CompletedTask; - } - private Task Client_GuildMemberUpdated(SocketGuildUser before, SocketGuildUser after) { var _ = Task.Run(async () => diff --git a/src/NadekoBot/Modules/Utility/Services/VerboseErrorsService.cs b/src/NadekoBot/Modules/Utility/Services/VerboseErrorsService.cs index cacdaaa49..6afe22fcf 100644 --- a/src/NadekoBot/Modules/Utility/Services/VerboseErrorsService.cs +++ b/src/NadekoBot/Modules/Utility/Services/VerboseErrorsService.cs @@ -11,7 +11,7 @@ using NadekoBot.Modules.Administration; namespace NadekoBot.Modules.Utility.Services { - public class VerboseErrorsService : INService, IUnloadableService + public class VerboseErrorsService : INService { private readonly ConcurrentHashSet guildsEnabled; private readonly DbService _db; @@ -32,12 +32,6 @@ namespace NadekoBot.Modules.Utility.Services .Select(x => x.GuildId)); } - public Task Unload() - { - _ch.CommandErrored -= LogVerboseError; - return Task.CompletedTask; - } - private async Task LogVerboseError(CommandInfo cmd, ITextChannel channel, string reason) { if (channel is null || !guildsEnabled.Contains(channel.GuildId)) diff --git a/src/NadekoBot/Modules/Xp/Services/XpService.cs b/src/NadekoBot/Modules/Xp/Services/XpService.cs index 831b46afe..a465e03cf 100644 --- a/src/NadekoBot/Modules/Xp/Services/XpService.cs +++ b/src/NadekoBot/Modules/Xp/Services/XpService.cs @@ -28,7 +28,7 @@ using Image = SixLabors.ImageSharp.Image; namespace NadekoBot.Modules.Xp.Services { - public class XpService : INService, IUnloadableService + public class XpService : INService { private enum NotifOf { @@ -1182,14 +1182,6 @@ namespace NadekoBot.Modules.Xp.Services } } - public Task Unload() - { - _cmd.OnMessageNoTrigger -= _cmd_OnMessageNoTrigger; - _client.UserVoiceStateUpdated -= _client_OnUserVoiceStateUpdated; - _client.GuildAvailable -= _client_OnGuildAvailable; - return Task.CompletedTask; - } - public void XpReset(ulong guildId, ulong userId) { using (var uow = _db.GetDbContext()) diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index bafd5dcc9..3521c4f11 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -42,6 +42,7 @@ + diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index fdb4f3290..8bea488b6 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -2,7 +2,6 @@ using Discord.Commands; using Discord.Net; using Discord.WebSocket; -using Microsoft.Extensions.DependencyInjection; using NadekoBot.Common.Collections; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Extensions; @@ -10,26 +9,16 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using NadekoBot.Common.Configs; -using NadekoBot.Services; using NadekoBot.Db; -using NadekoBot.Modules.Administration; using Serilog; namespace NadekoBot.Services { - public class GuildUserComparer : IEqualityComparer - { - public bool Equals(IGuildUser x, IGuildUser y) => x.Id == y.Id; - - public int GetHashCode(IGuildUser obj) => obj.Id.GetHashCode(); - } - public class CommandHandler : INService { public const int GlobalCommandsCooldown = 750; @@ -38,11 +27,8 @@ namespace NadekoBot.Services private readonly CommandService _commandService; private readonly BotConfigService _bss; private readonly Bot _bot; + private readonly IBehaviourExecutor _behaviourExecutor; private IServiceProvider _services; - private IEnumerable _earlyBehaviors; - private IEnumerable _inputTransformers; - private IEnumerable _lateBlockers; - private IEnumerable _lateExecutors; private ConcurrentDictionary _prefixes { get; } = new ConcurrentDictionary(); @@ -56,17 +42,24 @@ namespace NadekoBot.Services public ConcurrentHashSet UsersOnShortCooldown { get; } = new ConcurrentHashSet(); private readonly Timer _clearUsersOnShortCooldown; - public CommandHandler(DiscordSocketClient client, DbService db, CommandService commandService, - BotConfigService bss, Bot bot, IServiceProvider services) + // todo move behaviours to a separate service + public CommandHandler( + DiscordSocketClient client, + DbService db, + CommandService commandService, + BotConfigService bss, + Bot bot, + IBehaviourExecutor behaviourExecutor, + IServiceProvider services) { _client = client; _commandService = commandService; _bss = bss; _bot = bot; + _behaviourExecutor = behaviourExecutor; _db = db; _services = services; - _clearUsersOnShortCooldown = new Timer(_ => { UsersOnShortCooldown.Clear(); @@ -118,27 +111,6 @@ namespace NadekoBot.Services return prefix; } - - public void AddServices(IServiceCollection services) - { - _lateBlockers = services.Where(x => x.ImplementationType?.GetInterfaces().Contains(typeof(ILateBlocker)) ?? false) - .Select(x => _services.GetService(x.ImplementationType) as ILateBlocker) - .OrderByDescending(x => x.Priority) - .ToArray(); - - _lateExecutors = services.Where(x => x.ImplementationType?.GetInterfaces().Contains(typeof(ILateExecutor)) ?? false) - .Select(x => _services.GetService(x.ImplementationType) as ILateExecutor) - .ToArray(); - - _inputTransformers = services.Where(x => x.ImplementationType?.GetInterfaces().Contains(typeof(IInputTransformer)) ?? false) - .Select(x => _services.GetService(x.ImplementationType) as IInputTransformer) - .ToArray(); - - _earlyBehaviors = services.Where(x => x.ImplementationType?.GetInterfaces().Contains(typeof(IEarlyBehavior)) ?? false) - .Select(x => _services.GetService(x.ImplementationType) as IEarlyBehavior) - .ToArray(); - } - public async Task ExecuteExternal(ulong? guildId, ulong channelId, string commandText) { if (guildId != null) @@ -172,8 +144,7 @@ namespace NadekoBot.Services private Task LogSuccessfulExecution(IUserMessage usrMsg, ITextChannel channel, params int[] execPoints) { - var bss = _services.GetService(); - if (bss.Data.ConsoleOutputType == ConsoleOutputType.Normal) + if (_bss.GetRawData().ConsoleOutputType == ConsoleOutputType.Normal) { Log.Information($"Command Executed after " + string.Join("/", execPoints.Select(x => (x * _oneThousandth).ToString("F3"))) + "s\n\t" + "User: {0}\n\t" + @@ -199,8 +170,7 @@ namespace NadekoBot.Services private void LogErroredExecution(string errorMessage, IUserMessage usrMsg, ITextChannel channel, params int[] execPoints) { - var bss = _services.GetService(); - if (bss.Data.ConsoleOutputType == ConsoleOutputType.Normal) + if (_bss.GetRawData().ConsoleOutputType == ConsoleOutputType.Normal) { Log.Warning($"Command Errored after " + string.Join("/", execPoints.Select(x => (x * _oneThousandth).ToString("F3"))) + "s\n\t" + "User: {0}\n\t" + @@ -231,7 +201,7 @@ namespace NadekoBot.Services { try { - if (msg.Author.IsBot || !_bot.Ready.Task.IsCompleted) //no bots, wait until bot connected and initialized + if (msg.Author.IsBot || !_bot.IsReady) //no bots, wait until bot connected and initialized return; if (!(msg is SocketUserMessage usrMsg)) @@ -258,62 +228,33 @@ namespace NadekoBot.Services public async Task TryRunCommand(SocketGuild guild, ISocketMessageChannel channel, IUserMessage usrMsg) { - var execTime = Environment.TickCount; + var startTime = Environment.TickCount; - //its nice to have early blockers and early blocking executors separate, but - //i could also have one interface with priorities, and just put early blockers on - //highest priority. :thinking: - foreach (var beh in _earlyBehaviors) - { - if (await beh.RunBehavior(_client, guild, usrMsg).ConfigureAwait(false)) - { - if (beh.BehaviorType == ModuleBehaviorType.Blocker) - { - Log.Information("Blocked User: [{0}] Message: [{1}] Service: [{2}]", usrMsg.Author, - usrMsg.Content, beh.GetType().Name); - } - else if (beh.BehaviorType == ModuleBehaviorType.Executor) - { - Log.Information("User [{0}] executed [{1}] in [{2}]", usrMsg.Author, usrMsg.Content, - beh.GetType().Name); + var blocked = await _behaviourExecutor.RunEarlyBehavioursAsync(guild, usrMsg); + if (blocked) + return; - } - - return; - } - } - - var exec2 = Environment.TickCount - execTime; + var blockTime = Environment.TickCount - startTime; + var messageContent = await _behaviourExecutor.RunInputTransformersAsync(guild, usrMsg); - string messageContent = usrMsg.Content; - foreach (var exec in _inputTransformers) - { - string newContent; - if ((newContent = await exec.TransformInput(guild, usrMsg.Channel, usrMsg.Author, messageContent) - .ConfigureAwait(false)) != messageContent.ToLowerInvariant()) - { - messageContent = newContent; - break; - } - } var prefix = GetPrefix(guild?.Id); var isPrefixCommand = messageContent.StartsWith(".prefix", StringComparison.InvariantCultureIgnoreCase); // execute the command and measure the time it took if (messageContent.StartsWith(prefix, StringComparison.InvariantCulture) || isPrefixCommand) { var (Success, Error, Info) = await ExecuteCommandAsync(new CommandContext(_client, usrMsg), messageContent, isPrefixCommand ? 1 : prefix.Length, _services, MultiMatchHandling.Best).ConfigureAwait(false); - execTime = Environment.TickCount - execTime; + startTime = Environment.TickCount - startTime; if (Success) { - await LogSuccessfulExecution(usrMsg, channel as ITextChannel, exec2, execTime).ConfigureAwait(false); + await LogSuccessfulExecution(usrMsg, channel as ITextChannel, blockTime, startTime).ConfigureAwait(false); await CommandExecuted(usrMsg, Info).ConfigureAwait(false); return; } else if (Error != null) { - LogErroredExecution(Error, usrMsg, channel as ITextChannel, exec2, execTime); + LogErroredExecution(Error, usrMsg, channel as ITextChannel, blockTime, startTime); if (guild != null) await CommandErrored(Info, channel as ITextChannel, Error).ConfigureAwait(false); } @@ -323,11 +264,7 @@ namespace NadekoBot.Services await OnMessageNoTrigger(usrMsg).ConfigureAwait(false); } - foreach (var exec in _lateExecutors) - { - await exec.LateExecute(_client, guild, usrMsg).ConfigureAwait(false); - } - + await _behaviourExecutor.RunLateExecutorsAsync(guild, usrMsg); } public Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommandAsync(CommandContext context, string input, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) @@ -423,17 +360,9 @@ namespace NadekoBot.Services return (false, null, cmd); //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown."); - var commandName = cmd.Aliases.First(); - foreach (var exec in _lateBlockers) - { - if (await exec.TryBlockLate(_client, context, cmd.Module.GetTopLevelModule().Name, cmd) - .ConfigureAwait(false)) - { - Log.Information("Late blocking User [{0}] Command: [{1}] in [{2}]", context.User, commandName, - exec.GetType().Name); - return (false, null, cmd); - } - } + var blocked = await _behaviourExecutor.RunLateBlockersAsync(context, cmd); + if (blocked) + return (false, null, cmd); //If we get this far, at least one parse was successful. Execute the most likely overload. var chosenOverload = successfulParses[0]; diff --git a/src/NadekoBot/Services/IBehaviourExecutor.cs b/src/NadekoBot/Services/IBehaviourExecutor.cs new file mode 100644 index 000000000..96b179adf --- /dev/null +++ b/src/NadekoBot/Services/IBehaviourExecutor.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; + +namespace NadekoBot.Services +{ + public interface IBehaviourExecutor + { + public Task RunEarlyBehavioursAsync(SocketGuild guild, IUserMessage usrMsg); + public Task RunInputTransformersAsync(SocketGuild guild, IUserMessage usrMsg); + Task RunLateBlockersAsync(ICommandContext context, CommandInfo cmd); + Task RunLateExecutorsAsync(SocketGuild guild, IUserMessage usrMsg); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Services/INService.cs b/src/NadekoBot/Services/INService.cs index ba4d29c43..13bbbeea3 100644 --- a/src/NadekoBot/Services/INService.cs +++ b/src/NadekoBot/Services/INService.cs @@ -1,6 +1,4 @@ -using System.Threading.Tasks; - -namespace NadekoBot.Services +namespace NadekoBot.Services { /// /// All services must implement this interface in order to be auto-discovered by the DI system @@ -9,12 +7,4 @@ namespace NadekoBot.Services { } - - /// - /// All services which require cleanup after they are unloaded must implement this interface - /// - public interface IUnloadableService - { - Task Unload(); - } } diff --git a/src/NadekoBot/Services/Impl/BehaviorExecutor.cs b/src/NadekoBot/Services/Impl/BehaviorExecutor.cs new file mode 100644 index 000000000..9123836b1 --- /dev/null +++ b/src/NadekoBot/Services/Impl/BehaviorExecutor.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using NadekoBot.Common.ModuleBehaviors; +using NadekoBot.Extensions; +using Serilog; + +namespace NadekoBot.Services +{ + public sealed class BehaviorExecutor : IBehaviourExecutor + { + private readonly DiscordSocketClient _client; + private readonly IEnumerable _lateExecutors; + private readonly IEnumerable _lateBlockers; + private readonly IEnumerable _earlyBehaviors; + private readonly IEnumerable _transformers; + + public BehaviorExecutor( + DiscordSocketClient client, + IEnumerable lateExecutors, + IEnumerable lateBlockers, + IEnumerable earlyBehaviors, + IEnumerable transformers) + { + _client = client; + _lateExecutors = lateExecutors; + _lateBlockers = lateBlockers; + _earlyBehaviors = earlyBehaviors; + _transformers = transformers; + } + + // todo early behaviors should print for themselves + public async Task RunEarlyBehavioursAsync(SocketGuild guild, IUserMessage usrMsg) + { + foreach (var beh in _earlyBehaviors) + { + if (await beh.RunBehavior(guild, usrMsg)) + { + return true; + } + } + + return false; + } + + public async Task RunInputTransformersAsync(SocketGuild guild, IUserMessage usrMsg) + { + var messageContent = usrMsg.Content; + foreach (var exec in _transformers) + { + string newContent; + if ((newContent = await exec.TransformInput(guild, usrMsg.Channel, usrMsg.Author, messageContent)) + != messageContent.ToLowerInvariant()) + { + messageContent = newContent; + break; + } + } + + return messageContent; + } + + public async Task RunLateBlockersAsync(ICommandContext ctx, CommandInfo cmd) + { + foreach (var exec in _lateBlockers) + { + if (await exec.TryBlockLate(ctx, cmd.Module.GetTopLevelModule().Name, cmd)) + { + Log.Information("Late blocking User [{0}] Command: [{1}] in [{2}]", + ctx.User, + cmd.Aliases[0], + exec.GetType().Name); + return true; + } + } + + return false; + } + + public async Task RunLateExecutorsAsync(SocketGuild guild, IUserMessage usrMsg) + { + foreach (var exec in _lateExecutors) + { + try + { + await exec.LateExecute(guild, usrMsg).ConfigureAwait(false); + } + catch (Exception ex) + { + Log.Error(ex, "Error in {TypeName} late executor: {ErrorMessage}", + exec.GetType().Name, + ex.Message); + } + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index e76d50bda..3b212c622 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -24,7 +24,6 @@ namespace NadekoBot.Services private IDatabase _db => _con.GetDatabase(); private const string _basePath = "data/"; - private const string _oldBasePath = "data/images/"; private const string _cardsPath = "data/images/cards"; public ImageUrls ImageUrls { get; private set; } @@ -79,112 +78,11 @@ namespace NadekoBot.Services _con = con; _creds = creds; _http = new HttpClient(); - - Migrate(); + ImageUrls = JsonConvert.DeserializeObject( File.ReadAllText(Path.Combine(_basePath, "images.json"))); } - private void Migrate() - { - try - { - Migrate1(); - Migrate2(); - Migrate3(); - } - catch (Exception ex) - { - Log.Warning(ex.Message); - Log.Error("Something has been incorrectly formatted in your 'images.json' file.\n" + - "Use the 'images_example.json' file as reference to fix it and restart the bot."); - } - } - - private void Migrate1() - { - if (!File.Exists(Path.Combine(_oldBasePath, "images.json"))) - return; - Log.Information("Migrating images v0 to images v1."); - // load old images - var oldUrls = JsonConvert.DeserializeObject( - File.ReadAllText(Path.Combine(_oldBasePath, "images.json"))); - // load new images - var newUrls = JsonConvert.DeserializeObject( - File.ReadAllText(Path.Combine(_basePath, "images.json"))); - - //swap new links with old ones if set. Also update old links. - newUrls.Coins = oldUrls.Coins; - - newUrls.Currency = oldUrls.Currency; - newUrls.Dice = oldUrls.Dice; - newUrls.Rategirl = oldUrls.Rategirl; - newUrls.Xp = oldUrls.Xp; - newUrls.Version = 1; - - File.WriteAllText(Path.Combine(_basePath, "images.json"), - JsonConvert.SerializeObject(newUrls, Formatting.Indented)); - File.Delete(Path.Combine(_oldBasePath, "images.json")); - } - - private void Migrate2() - { - // load new images - var urls = JsonConvert.DeserializeObject(File.ReadAllText(Path.Combine(_basePath, "images.json"))); - - if (urls.Version >= 2) - return; - Log.Information("Migrating images v1 to images v2."); - urls.Version = 2; - - var prefix = $"{_creds.RedisKey()}_localimg_"; - _db.KeyDelete(new[] { - prefix + "heads", - prefix + "tails", - prefix + "dice", - prefix + "slot_background", - prefix + "slotnumbers", - prefix + "slotemojis", - prefix + "wife_matrix", - prefix + "rategirl_dot", - prefix + "xp_card", - prefix + "rip", - prefix + "rip_overlay" } - .Select(x => (RedisKey)x).ToArray()); - - File.WriteAllText(Path.Combine(_basePath, "images.json"), JsonConvert.SerializeObject(urls, Formatting.Indented)); - } - - private void Migrate3() - { - var urls = JsonConvert.DeserializeObject( - File.ReadAllText(Path.Combine(_basePath, "images.json"))); - - if (urls.Version >= 3) - return; - urls.Version = 3; - Log.Information("Migrating images v2 to images v3."); - - var baseStr = "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/currency/"; - - var replacementTable = new Dictionary() - { - {new Uri(baseStr + "0.jpg"), new Uri(baseStr + "0.png") }, - {new Uri(baseStr + "1.jpg"), new Uri(baseStr + "1.png") }, - {new Uri(baseStr + "2.jpg"), new Uri(baseStr + "2.png") } - }; - - if (replacementTable.Keys.Any(x => urls.Currency.Contains(x))) - { - urls.Currency = urls.Currency.Select(x => replacementTable.TryGetValue(x, out var newUri) - ? newUri - : x).Append(new Uri(baseStr + "3.png")) - .ToArray(); - } - - File.WriteAllText(Path.Combine(_basePath, "images.json"), JsonConvert.SerializeObject(urls, Formatting.Indented)); - } - public async Task AllKeysExist() { try