Add callback method for when a module class has been added (#934)
commit 5b047bf02b4299f34172cac05dc7e4a84ecc108c Author: Joe4evr <jii.geugten@gmail.com> Date: Fri Feb 2 22:22:00 2018 +0100 [feature/OnModuleAdded] Quickstart fixes (#946) * Quickstart: fix minor derp * Other overdue fixes commit bd3e9eee943b9092cc45217b19ff95bae359f888 Author: Christopher F <computerizedtaco@gmail.com> Date: Sat Jan 27 16:51:18 2018 -0500 Resort usings in ModuleBase commit 8042767579b337fdae7fe48e0a6ea2f007aef440 Author: Christopher F <computerizedtaco@gmail.com> Date: Sat Jan 27 16:41:39 2018 -0500 Clean up removed owned IServiceProvider commit 30066cb102ffbd65906ead72a377811aa501abba Author: Christopher F <computerizedtaco@gmail.com> Date: Sat Jan 27 16:37:22 2018 -0500 Remove redundant try-catch around OnModuleBuilding invocation If this exception is going to be rethrown, there's no reason to include a try-catch. commit 60c7c31d4476c498a97ae0536ec5792f08efb89b Author: Christopher F <computerizedtaco@gmail.com> Date: Sat Jan 27 16:36:27 2018 -0500 Include the ModuleBuilder in OnModuleBuilding This allows modules hooking into OnModuleBuilding method to mutate theirselves at runtime. commit b6a9ff57860ff3bddbad7ca850fd331529cb8e6e Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 22 13:17:14 2018 +0100 #DERP commit f623d19c68c5642a44898a561f77ed82d53fd103 Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 22 13:15:31 2018 +0100 Resolution for #937 because it's literally 4 lines of code commit 8272c9675b0d63b4100aaf57f5067d635b68f5e6 Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 22 11:39:28 2018 +0100 Re-adjust quickstart commit e30b9071351b69baa30a93a4851516dca9ea43cf Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 22 11:35:08 2018 +0100 Undo experimental changes, request IServiceProvider instance everywhere instead commit ad7e0a46c8709e845dfacdc298a893e22dc11567 Author: Joe4evr <jii.geugten@gmail.com> Date: Fri Jan 19 03:40:27 2018 +0100 Fix quickstart leftover from previous draft commit e3349ef3d400bb3ad8cb28dd4234d5316a80bcc4 Author: Joe4evr <jii.geugten@gmail.com> Date: Fri Jan 19 03:33:46 2018 +0100 Doc comment on items commit 81bd9111faaf98a52679daae863ab04dce96e63e Author: Joe4evr <jii.geugten@gmail.com> Date: Fri Jan 19 03:16:44 2018 +0100 Add comment about the ServiceProviderFactory in the quickstart commit 72b5e6c8a149d8e989b46351965daa14f8ca318c Author: Joe4evr <jii.geugten@gmail.com> Date: Fri Jan 19 03:10:40 2018 +0100 Remove superfluous comments, provide simpler alternative for setting the ServiceProvider. commit 74b17b0e04e2c413397a2e1b66ff814615326205 Author: Joe4evr <jii.geugten@gmail.com> Date: Tue Jan 16 18:06:28 2018 +0100 Experimental change for feedback commit 7b100e99bb119be190006d1cd8e403776930e401 Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 15 23:34:06 2018 +0100 * Make the service provider parameters required * Adjust quickstart guide to reflect changes commit 7f1b792946ac6b950922b06178aa5cc37d9f4144 Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 15 20:04:37 2018 +0100 I..... missed one. commit 031b289d80604666dde62619e521af303203d48d Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 15 20:02:20 2018 +0100 Rename method to more intuitive 'OnModuleBuilding' commit 9a166ef1d0baecd21e4e5b965e2ac364feddbe2e Author: Joe4evr <jii.geugten@gmail.com> Date: Mon Jan 15 19:09:10 2018 +0100 Add callback method for when a module class has been added to the CommandService.
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
using Discord.Commands.Builders;
|
||||
using Discord.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -8,6 +6,9 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Discord.Commands.Builders;
|
||||
using Discord.Logging;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
@@ -85,7 +86,8 @@ namespace Discord.Commands
|
||||
var builder = new ModuleBuilder(this, null, primaryAlias);
|
||||
buildFunc(builder);
|
||||
|
||||
var module = builder.Build(this);
|
||||
var module = builder.Build(this, null);
|
||||
|
||||
return LoadModuleInternal(module);
|
||||
}
|
||||
finally
|
||||
@@ -93,8 +95,8 @@ namespace Discord.Commands
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
public Task<ModuleInfo> AddModuleAsync<T>() => AddModuleAsync(typeof(T));
|
||||
public async Task<ModuleInfo> AddModuleAsync(Type type)
|
||||
public Task<ModuleInfo> AddModuleAsync<T>(IServiceProvider services) => AddModuleAsync(typeof(T), services);
|
||||
public async Task<ModuleInfo> AddModuleAsync(Type type, IServiceProvider services)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
@@ -104,7 +106,7 @@ namespace Discord.Commands
|
||||
if (_typedModuleDefs.ContainsKey(type))
|
||||
throw new ArgumentException($"This module has already been added.");
|
||||
|
||||
var module = (await ModuleClassBuilder.BuildAsync(this, typeInfo).ConfigureAwait(false)).FirstOrDefault();
|
||||
var module = (await ModuleClassBuilder.BuildAsync(this, services, typeInfo).ConfigureAwait(false)).FirstOrDefault();
|
||||
|
||||
if (module.Value == default(ModuleInfo))
|
||||
throw new InvalidOperationException($"Could not build the module {type.FullName}, did you pass an invalid type?");
|
||||
@@ -118,13 +120,13 @@ namespace Discord.Commands
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
public async Task<IEnumerable<ModuleInfo>> AddModulesAsync(Assembly assembly)
|
||||
public async Task<IEnumerable<ModuleInfo>> AddModulesAsync(Assembly assembly, IServiceProvider services)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var types = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false);
|
||||
var moduleDefs = await ModuleClassBuilder.BuildAsync(types, this).ConfigureAwait(false);
|
||||
var moduleDefs = await ModuleClassBuilder.BuildAsync(types, this, services).ConfigureAwait(false);
|
||||
|
||||
foreach (var info in moduleDefs)
|
||||
{
|
||||
@@ -224,7 +226,7 @@ namespace Discord.Commands
|
||||
var readers = _typeReaders.GetOrAdd(typeof(Nullable<>).MakeGenericType(valueType), x => new ConcurrentDictionary<Type, TypeReader>());
|
||||
var nullableReader = NullableTypeReader.Create(valueType, valueTypeReader);
|
||||
readers[nullableReader.GetType()] = nullableReader;
|
||||
}
|
||||
}
|
||||
internal IDictionary<Type, TypeReader> GetTypeReaders(Type type)
|
||||
{
|
||||
if (_typeReaders.TryGetValue(type, out var definedTypeReaders))
|
||||
@@ -277,92 +279,94 @@ namespace Discord.Commands
|
||||
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
||||
{
|
||||
services = services ?? EmptyServiceProvider.Instance;
|
||||
|
||||
var searchResult = Search(context, input);
|
||||
if (!searchResult.IsSuccess)
|
||||
return searchResult;
|
||||
|
||||
var commands = searchResult.Commands;
|
||||
var preconditionResults = new Dictionary<CommandMatch, PreconditionResult>();
|
||||
|
||||
foreach (var match in commands)
|
||||
using (var scope = services.CreateScope())
|
||||
{
|
||||
preconditionResults[match] = await match.Command.CheckPreconditionsAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
var searchResult = Search(context, input);
|
||||
if (!searchResult.IsSuccess)
|
||||
return searchResult;
|
||||
|
||||
var successfulPreconditions = preconditionResults
|
||||
.Where(x => x.Value.IsSuccess)
|
||||
.ToArray();
|
||||
var commands = searchResult.Commands;
|
||||
var preconditionResults = new Dictionary<CommandMatch, PreconditionResult>();
|
||||
|
||||
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 bestCandidate.Value;
|
||||
}
|
||||
|
||||
//If we get this far, at least one precondition was successful.
|
||||
|
||||
var parseResultsDict = new Dictionary<CommandMatch, ParseResult>();
|
||||
foreach (var pair in successfulPreconditions)
|
||||
{
|
||||
var parseResult = await pair.Key.ParseAsync(context, searchResult, pair.Value, services).ConfigureAwait(false);
|
||||
|
||||
if (parseResult.Error == CommandError.MultipleMatches)
|
||||
foreach (var match in commands)
|
||||
{
|
||||
IReadOnlyList<TypeReaderValue> argList, paramList;
|
||||
switch (multiMatchHandling)
|
||||
preconditionResults[match] = await match.Command.CheckPreconditionsAsync(context, scope.ServiceProvider).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
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 bestCandidate.Value;
|
||||
}
|
||||
|
||||
//If we get this far, at least one precondition was successful.
|
||||
|
||||
var parseResultsDict = new Dictionary<CommandMatch, ParseResult>();
|
||||
foreach (var pair in successfulPreconditions)
|
||||
{
|
||||
var parseResult = await pair.Key.ParseAsync(context, searchResult, pair.Value, scope.ServiceProvider).ConfigureAwait(false);
|
||||
|
||||
if (parseResult.Error == CommandError.MultipleMatches)
|
||||
{
|
||||
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;
|
||||
IReadOnlyList<TypeReaderValue> 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;
|
||||
}
|
||||
|
||||
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)
|
||||
// Calculates the 'score' of a command given a parse result
|
||||
float CalculateScore(CommandMatch match, ParseResult parseResult)
|
||||
{
|
||||
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;
|
||||
float argValuesScore = 0, paramValuesScore = 0;
|
||||
|
||||
argValuesScore = argValuesSum / match.Command.Parameters.Count;
|
||||
paramValuesScore = paramValuesSum / match.Command.Parameters.Count;
|
||||
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;
|
||||
}
|
||||
|
||||
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 bestMatch.Value;
|
||||
}
|
||||
|
||||
//If we get this far, at least one parse was successful. Execute the most likely overload.
|
||||
var chosenOverload = successfulParses[0];
|
||||
return await chosenOverload.Key.ExecuteAsync(context, chosenOverload.Value, scope.ServiceProvider).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//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 bestMatch.Value;
|
||||
}
|
||||
|
||||
//If we get this far, at least one parse was successful. Execute the most likely overload.
|
||||
var chosenOverload = successfulParses[0];
|
||||
return await chosenOverload.Key.ExecuteAsync(context, chosenOverload.Value, services).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user