- Started cleanup of command handler

- Removed IUnloadableService
- Started removing INService (removed it from services which implement behavior interfaces) - wip
- Added scrutor for better service registration - wip
This commit is contained in:
Kwoth
2021-06-28 23:20:02 +02:00
parent 1e90d7f7bb
commit 3c82c1f919
30 changed files with 217 additions and 360 deletions

View File

@@ -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<IGuildUser>
{
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<IEarlyBehavior> _earlyBehaviors;
private IEnumerable<IInputTransformer> _inputTransformers;
private IEnumerable<ILateBlocker> _lateBlockers;
private IEnumerable<ILateExecutor> _lateExecutors;
private ConcurrentDictionary<ulong, string> _prefixes { get; } = new ConcurrentDictionary<ulong, string>();
@@ -56,17 +42,24 @@ namespace NadekoBot.Services
public ConcurrentHashSet<ulong> UsersOnShortCooldown { get; } = new ConcurrentHashSet<ulong>();
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<BotConfigService>();
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<BotConfigService>();
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];