1
Fork 0
mirror of https://gitlab.com/Kwoth/nadekobot.git synced 2024-10-02 20:13:13 +00:00

Re-added medusa system, but untested

This commit is contained in:
Kwoth 2024-05-04 08:08:13 +00:00
parent ea0b51d474
commit 6a7ab79446
8 changed files with 152 additions and 373 deletions

View file

@ -1,65 +1,61 @@
// using Discord; using Discord;
//
// namespace NadekoBot.Medusa; namespace NadekoBot.Medusa;
//
// public static class MedusaExtensions public static class MedusaExtensions
// { {
// public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "") public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "")
// => ch.SendMessageAsync(msg, => ch.SendMessageAsync(msg,
// embed: embed.Build(), embed: embed.Build(),
// options: new() options: new()
// { {
// RetryMode = RetryMode.Retry502 RetryMode = RetryMode.Retry502
// }); });
//
// // unlocalized // unlocalized
// public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, AnyContext ctx, string msg) public static Task<IUserMessage> SendConfirmAsync(this AnyContext ctx, string msg)
// => _sender.Response(ch).Embed(ctx.Embed().WithOkColor().WithDescription(msg)).SendAsync(); => ctx.Channel.EmbedAsync(new EmbedBuilder()
// .WithColor(0, 200, 0)
// public static Task<IUserMessage> SendPendingAsync(this IMessageChannel ch, AnyContext ctx, string msg) .WithDescription(msg));
// => _sender.Response(ch).Embed(ctx.Embed().WithPendingColor().WithDescription(msg)).SendAsync();
// public static Task<IUserMessage> SendPendingAsync(this AnyContext ctx, string msg)
// public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, AnyContext ctx, string msg) => ctx.Channel.EmbedAsync(new EmbedBuilder()
// => _sender.Response(ch).Embed(ctx.Embed().WithErrorColor().WithDescription(msg)).SendAsync(); .WithColor(200, 200, 0)
// .WithDescription(msg));
// // unlocalized
// public static Task<IUserMessage> SendConfirmAsync(this AnyContext ctx, string msg) public static Task<IUserMessage> SendErrorAsync(this AnyContext ctx, string msg)
// => ctx.Channel.SendConfirmAsync(ctx, msg); => ctx.Channel.EmbedAsync(new EmbedBuilder()
// .WithColor(200, 0, 0)
// public static Task<IUserMessage> SendPendingAsync(this AnyContext ctx, string msg) .WithDescription(msg));
// => ctx.Channel.SendPendingAsync(ctx, msg);
// // localized
// public static Task<IUserMessage> SendErrorAsync(this AnyContext ctx, string msg) public static Task ConfirmAsync(this AnyContext ctx)
// => ctx.Channel.SendErrorAsync(ctx, msg); => ctx.Message.AddReactionAsync(new Emoji("✅"));
//
// // localized public static Task ErrorAsync(this AnyContext ctx)
// public static Task ConfirmAsync(this AnyContext ctx) => ctx.Message.AddReactionAsync(new Emoji("❌"));
// => ctx.Message.AddReactionAsync(new Emoji("✅"));
// public static Task WarningAsync(this AnyContext ctx)
// public static Task ErrorAsync(this AnyContext ctx) => ctx.Message.AddReactionAsync(new Emoji("⚠️"));
// => ctx.Message.AddReactionAsync(new Emoji("❌"));
// public static Task WaitAsync(this AnyContext ctx)
// public static Task WarningAsync(this AnyContext ctx) => ctx.Message.AddReactionAsync(new Emoji("🤔"));
// => ctx.Message.AddReactionAsync(new Emoji("⚠️"));
// public static Task<IUserMessage> ErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
// public static Task WaitAsync(this AnyContext ctx) => ctx.SendErrorAsync(ctx.GetText(key, args));
// => ctx.Message.AddReactionAsync(new Emoji("🤔"));
// public static Task<IUserMessage> PendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
// public static Task<IUserMessage> ErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args) => ctx.SendPendingAsync(ctx.GetText(key, args));
// => ctx.SendErrorAsync(ctx.GetText(key, args));
// public static Task<IUserMessage> ConfirmLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
// public static Task<IUserMessage> PendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args) => ctx.SendConfirmAsync(ctx.GetText(key, args));
// => ctx.SendPendingAsync(ctx.GetText(key, args));
// public static Task<IUserMessage> ReplyErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
// public static Task<IUserMessage> ConfirmLocalizedAsync(this AnyContext ctx, string key, params object[]? args) => ctx.SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
// => ctx.SendConfirmAsync(ctx.GetText(key, args));
// public static Task<IUserMessage> ReplyPendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
// public static Task<IUserMessage> ReplyErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args) => ctx.SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
// => ctx.SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
// public static Task<IUserMessage> ReplyConfirmLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
// public static Task<IUserMessage> ReplyPendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args) => ctx.SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
// => ctx.SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}"); }
//
// public static Task<IUserMessage> ReplyConfirmLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
// => ctx.SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
// }

View file

@ -132,7 +132,6 @@ public sealed class Bot : IBot
foreach (var a in _loadedAssemblies) foreach (var a in _loadedAssemblies)
{ {
svcs.AddConfigServices(a) svcs.AddConfigServices(a)
.AddConfigMigrators(a)
.AddLifetimeServices(a); .AddLifetimeServices(a);
} }
@ -157,9 +156,6 @@ public sealed class Bot : IBot
Services = svcs; Services = svcs;
Services.GetRequiredService<IBehaviorHandler>().Initialize(); Services.GetRequiredService<IBehaviorHandler>().Initialize();
if (Client.ShardId == 0)
ApplyConfigMigrations();
foreach (var a in _loadedAssemblies) foreach (var a in _loadedAssemblies)
{ {
LoadTypeReaders(a); LoadTypeReaders(a);
@ -169,14 +165,6 @@ public sealed class Bot : IBot
Log.Information("All services loaded in {ServiceLoadTime:F2}s", sw.Elapsed.TotalSeconds); Log.Information("All services loaded in {ServiceLoadTime:F2}s", sw.Elapsed.TotalSeconds);
} }
private void ApplyConfigMigrations()
{
// execute all migrators
var migrators = Services.GetServices<IConfigMigrator>();
foreach (var migrator in migrators)
migrator.EnsureMigrated();
}
private void LoadTypeReaders(Assembly assembly) private void LoadTypeReaders(Assembly assembly)
{ {
var filteredTypes = assembly.GetExportedTypes() var filteredTypes = assembly.GetExportedTypes()

View file

@ -1,22 +1,27 @@
using System.Reflection; using DryIoc;
using Ninject; using System.Reflection;
using Ninject.Activation;
using Ninject.Activation.Caching;
using Ninject.Modules;
using Ninject.Planning;
using System.Text.Json; using System.Text.Json;
namespace NadekoBot.Medusa; namespace NadekoBot.Medusa;
public sealed class MedusaNinjectModule : NinjectModule public interface IIocModule
{ {
public override string Name { get; } public string Name { get; }
public void Load();
public void Unload();
}
public sealed class MedusaNinjectIocModule : IIocModule, IDisposable
{
public string Name { get; }
private volatile bool isLoaded = false; private volatile bool isLoaded = false;
private readonly Dictionary<Type, Type[]> _types; private readonly Dictionary<Type, Type[]> _types;
private readonly IContainer _cont;
public MedusaNinjectModule(Assembly assembly, string name) public MedusaNinjectIocModule(IContainer cont, Assembly assembly, string name)
{ {
Name = name; Name = name;
_cont = cont;
_types = assembly.GetExportedTypes() _types = assembly.GetExportedTypes()
.Where(t => t.IsClass) .Where(t => t.IsClass)
.Where(t => t.GetCustomAttribute<svcAttribute>() is not null) .Where(t => t.GetCustomAttribute<svcAttribute>() is not null)
@ -24,7 +29,7 @@ public sealed class MedusaNinjectModule : NinjectModule
type => type.GetInterfaces().ToArray()); type => type.GetInterfaces().ToArray());
} }
public override void Load() public void Load()
{ {
if (isLoaded) if (isLoaded)
return; return;
@ -32,59 +37,27 @@ public sealed class MedusaNinjectModule : NinjectModule
foreach (var (type, data) in _types) foreach (var (type, data) in _types)
{ {
var attribute = type.GetCustomAttribute<svcAttribute>()!; var attribute = type.GetCustomAttribute<svcAttribute>()!;
var scope = GetScope(attribute.Lifetime);
Bind(type) var reuse = attribute.Lifetime == Lifetime.Singleton
.ToSelf() ? Reuse.Singleton
.InScope(scope); : Reuse.Transient;
foreach (var inter in data) _cont.RegisterMany([type], reuse);
{
Bind(inter)
.ToMethod(x => x.Kernel.Get(type))
.InScope(scope);
}
} }
isLoaded = true; isLoaded = true;
} }
private Func<IContext, object?> GetScope(Lifetime lt) public void Unload()
=> _ => lt switch
{
Lifetime.Singleton => this,
Lifetime.Transient => null,
_ => null,
};
public override void Unload()
{ {
if (!isLoaded) if (!isLoaded)
return; return;
var planner = (RemovablePlanner)Kernel!.Components.Get<IPlanner>(); foreach (var type in _types.Keys)
var cache = Kernel.Components.Get<ICache>();
foreach (var binding in this.Bindings)
{ {
Kernel.RemoveBinding(binding); _cont.Unregister(type);
} }
foreach (var type in _types.SelectMany(x => x.Value).Concat(_types.Keys))
{
var binds = Kernel.GetBindings(type);
if (!binds.Any())
{
Unbind(type);
planner.RemovePlan(type);
}
}
Bindings.Clear();
cache.Clear(this);
_types.Clear(); _types.Clear();
// in case the library uses System.Text.Json // in case the library uses System.Text.Json
@ -95,4 +68,7 @@ public sealed class MedusaNinjectModule : NinjectModule
isLoaded = false; isLoaded = false;
} }
public void Dispose()
=> _types.Clear();
} }

View file

@ -4,8 +4,6 @@ using Microsoft.Extensions.DependencyInjection;
using Nadeko.Common.Medusa; using Nadeko.Common.Medusa;
using Nadeko.Medusa.Adapters; using Nadeko.Medusa.Adapters;
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using Ninject;
using Ninject.Modules;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
@ -21,7 +19,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
private readonly IBehaviorHandler _behHandler; private readonly IBehaviorHandler _behHandler;
private readonly IPubSub _pubSub; private readonly IPubSub _pubSub;
private readonly IMedusaConfigService _medusaConfig; private readonly IMedusaConfigService _medusaConfig;
private readonly IContainer _kernel; private readonly IContainer _cont;
private readonly ConcurrentDictionary<string, ResolvedMedusa> _resolved = new(); private readonly ConcurrentDictionary<string, ResolvedMedusa> _resolved = new();
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
@ -35,7 +33,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
public MedusaLoaderService( public MedusaLoaderService(
CommandService cmdService, CommandService cmdService,
IContainer kernel, IContainer cont,
IBehaviorHandler behHandler, IBehaviorHandler behHandler,
IPubSub pubSub, IPubSub pubSub,
IMedusaConfigService medusaConfig) IMedusaConfigService medusaConfig)
@ -44,7 +42,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
_behHandler = behHandler; _behHandler = behHandler;
_pubSub = pubSub; _pubSub = pubSub;
_medusaConfig = medusaConfig; _medusaConfig = medusaConfig;
_kernel = kernel; _cont = cont;
// has to be done this way to support this feature on sharded bots // has to be done this way to support this feature on sharded bots
_pubSub.Sub(_loadKey, async name => await InternalLoadAsync(name)); _pubSub.Sub(_loadKey, async name => await InternalLoadAsync(name));
@ -200,7 +198,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
if (LoadAssemblyInternal(safeName, if (LoadAssemblyInternal(safeName,
out var ctx, out var ctx,
out var snekData, out var snekData,
out var kernelModule, out var iocModule,
out var strings, out var strings,
out var typeReaders)) out var typeReaders))
{ {
@ -219,7 +217,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
await sub.Instance.InitializeAsync(); await sub.Instance.InitializeAsync();
} }
var module = await LoadModuleInternalAsync(name, point, strings, kernelModule); var module = await LoadModuleInternalAsync(name, point, strings, iocModule);
moduleInfos.Add(module); moduleInfos.Add(module);
} }
catch (Exception ex) catch (Exception ex)
@ -240,7 +238,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
typeReaders, typeReaders,
execs) execs)
{ {
KernelModule = kernelModule IocModule = iocModule
}; };
@ -273,11 +271,11 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
var behs = new List<ICustomBehavior>(); var behs = new List<ICustomBehavior>();
foreach (var snek in snekData) foreach (var snek in snekData)
{ {
behs.Add(new BehaviorAdapter(new(snek.Instance), strings, _kernel)); behs.Add(new BehaviorAdapter(new(snek.Instance), strings, _cont));
foreach (var sub in snek.Subsneks) foreach (var sub in snek.Subsneks)
{ {
behs.Add(new BehaviorAdapter(new(sub.Instance), strings, _kernel)); behs.Add(new BehaviorAdapter(new(sub.Instance), strings, _cont));
} }
} }
@ -315,7 +313,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
string safeName, string safeName,
[NotNullWhen(true)] out WeakReference<MedusaAssemblyLoadContext>? ctxWr, [NotNullWhen(true)] out WeakReference<MedusaAssemblyLoadContext>? ctxWr,
[NotNullWhen(true)] out IReadOnlyCollection<SnekInfo>? snekData, [NotNullWhen(true)] out IReadOnlyCollection<SnekInfo>? snekData,
[NotNullWhen(true)] out INinjectModule? ninjectModule, [NotNullWhen(true)] out IIocModule? iocModule,
out IMedusaStrings strings, out IMedusaStrings strings,
out Dictionary<Type, TypeReader> typeReaders) out Dictionary<Type, TypeReader> typeReaders)
{ {
@ -337,18 +335,15 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
ctx.LoadDependencies(a); ctx.LoadDependencies(a);
// load services // load services
ninjectModule = new MedusaNinjectModule(a, safeName); iocModule = new MedusaNinjectIocModule(_cont, a, safeName);
iocModule.Load();
// todo medusa won't work, uncomment
// _kernel.Load(ninjectModule);
var sis = LoadSneksFromAssembly(safeName, a); var sis = LoadSneksFromAssembly(safeName, a);
typeReaders = LoadTypeReadersFromAssembly(a, strings); typeReaders = LoadTypeReadersFromAssembly(a, strings);
if (sis.Count == 0) if (sis.Count == 0)
{ {
// todo uncomment iocModule.Unload();
// _kernel.Unload(safeName);
return false; return false;
} }
@ -375,12 +370,12 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
var typeReaders = new Dictionary<Type, TypeReader>(); var typeReaders = new Dictionary<Type, TypeReader>();
foreach (var parserType in paramParsers) foreach (var parserType in paramParsers)
{ {
var parserObj = ActivatorUtilities.CreateInstance(_kernel, parserType); var parserObj = ActivatorUtilities.CreateInstance(_cont, parserType);
var targetType = parserType.BaseType!.GetGenericArguments()[0]; var targetType = parserType.BaseType!.GetGenericArguments()[0];
var typeReaderInstance = (TypeReader)Activator.CreateInstance( var typeReaderInstance = (TypeReader)Activator.CreateInstance(
typeof(ParamParserAdapter<>).MakeGenericType(targetType), typeof(ParamParserAdapter<>).MakeGenericType(targetType),
args: new[] { parserObj, strings, _kernel })!; args: new[] { parserObj, strings, _cont })!;
typeReaders.Add(targetType, typeReaderInstance); typeReaders.Add(targetType, typeReaderInstance);
} }
@ -393,7 +388,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
string medusaName, string medusaName,
SnekInfo snekInfo, SnekInfo snekInfo,
IMedusaStrings strings, IMedusaStrings strings,
INinjectModule services) IIocModule services)
{ {
var module = await _cmdService.CreateModuleAsync(snekInfo.Instance.Prefix, var module = await _cmdService.CreateModuleAsync(snekInfo.Instance.Prefix,
CreateModuleFactory(medusaName, snekInfo, strings, services)); CreateModuleFactory(medusaName, snekInfo, strings, services));
@ -406,7 +401,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
string medusaName, string medusaName,
SnekInfo snekInfo, SnekInfo snekInfo,
IMedusaStrings strings, IMedusaStrings strings,
INinjectModule kernelModule) IIocModule iocModule)
=> mb => => mb =>
{ {
var m = mb.WithName(snekInfo.Name); var m = mb.WithName(snekInfo.Name);
@ -427,7 +422,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
} }
foreach (var subInfo in snekInfo.Subsneks) foreach (var subInfo in snekInfo.Subsneks)
m.AddModule(subInfo.Instance.Prefix, CreateModuleFactory(medusaName, subInfo, strings, kernelModule)); m.AddModule(subInfo.Instance.Prefix, CreateModuleFactory(medusaName, subInfo, strings, iocModule));
}; };
private static readonly RequireContextAttribute _reqGuild = new RequireContextAttribute(ContextType.Guild); private static readonly RequireContextAttribute _reqGuild = new RequireContextAttribute(ContextType.Guild);
@ -511,7 +506,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
return; return;
} }
var paramObjs = ParamObjs(contextType, cmdData, parameters, context, svcs, _kernel, strings); var paramObjs = ParamObjs(contextType, cmdData, parameters, context, svcs, _cont, strings);
try try
{ {
@ -605,12 +600,11 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
await DisposeSnekInstances(lsi); await DisposeSnekInstances(lsi);
var lc = lsi.LoadContext; var lc = lsi.LoadContext;
var km = lsi.KernelModule; var km = lsi.IocModule;
lsi.KernelModule = null!;
// todo uncomment
// _kernel.Unload(km.Name);
lsi.IocModule.Unload();
lsi.IocModule = null!;
if (km is IDisposable d) if (km is IDisposable d)
d.Dispose(); d.Dispose();
@ -748,7 +742,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
var filters = type.GetCustomAttributes<FilterAttribute>(true) var filters = type.GetCustomAttributes<FilterAttribute>(true)
.ToArray(); .ToArray();
var instance = (Snek)ActivatorUtilities.CreateInstance(_kernel, type); var instance = (Snek)ActivatorUtilities.CreateInstance(_cont, type);
var module = new SnekInfo(instance.Name, var module = new SnekInfo(instance.Name,
parentData, parentData,

View file

@ -1,6 +1,4 @@
using NadekoBot.Medusa; using System.Collections.Immutable;
using Ninject.Modules;
using System.Collections.Immutable;
namespace NadekoBot.Medusa; namespace NadekoBot.Medusa;
@ -13,5 +11,5 @@ public sealed record ResolvedMedusa(
IReadOnlyCollection<ICustomBehavior> Execs IReadOnlyCollection<ICustomBehavior> Execs
) )
{ {
public required INinjectModule KernelModule { get; set; } public required IIocModule IocModule { get; set; }
} }

View file

@ -1,122 +0,0 @@
//-------------------------------------------------------------------------------
// <copyright file="Planner.cs" company="Ninject Project Contributors">
// Copyright (c) 2007-2009, Enkari, Ltd.
// Copyright (c) 2009-2011 Ninject Project Contributors
// Authors: Nate Kohari (nate@enkari.com)
// Remo Gloor (remo.gloor@gmail.com)
//
// Dual-licensed under the Apache License, Version 2.0, and the Microsoft Public License (Ms-PL).
// you may not use this file except in compliance with one of the Licenses.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// or
// http://www.microsoft.com/opensource/licenses.mspx
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-------------------------------------------------------------------------------
// ReSharper disable all
#pragma warning disable
namespace Ninject.Planning;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Ninject.Components;
using Ninject.Infrastructure.Language;
using Ninject.Planning.Strategies;
/// <summary>
/// Generates plans for how to activate instances.
/// </summary>
public class RemovablePlanner : NinjectComponent, IPlanner
{
private readonly ReaderWriterLock plannerLock = new ReaderWriterLock();
private readonly Dictionary<Type, IPlan> plans = new Dictionary<Type, IPlan>();
/// <summary>
/// Initializes a new instance of the <see cref="RemovablePlanner"/> class.
/// </summary>
/// <param name="strategies">The strategies to execute during planning.</param>
public RemovablePlanner(IEnumerable<IPlanningStrategy> strategies)
{
this.Strategies = strategies.ToList();
}
/// <summary>
/// Gets the strategies that contribute to the planning process.
/// </summary>
public IList<IPlanningStrategy> Strategies { get; private set; }
/// <summary>
/// Gets or creates an activation plan for the specified type.
/// </summary>
/// <param name="type">The type for which a plan should be created.</param>
/// <returns>The type's activation plan.</returns>
public IPlan GetPlan(Type type)
{
this.plannerLock.AcquireReaderLock(Timeout.Infinite);
try
{
IPlan plan;
return this.plans.TryGetValue(type, out plan) ? plan : this.CreateNewPlan(type);
}
finally
{
this.plannerLock.ReleaseReaderLock();
}
}
/// <summary>
/// Creates an empty plan for the specified type.
/// </summary>
/// <param name="type">The type for which a plan should be created.</param>
/// <returns>The created plan.</returns>
protected virtual IPlan CreateEmptyPlan(Type type)
{
return new Plan(type);
}
/// <summary>
/// Creates a new plan for the specified type.
/// This method requires an active reader lock!
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The newly created plan.</returns>
private IPlan CreateNewPlan(Type type)
{
var lockCooki = this.plannerLock.UpgradeToWriterLock(Timeout.Infinite);
try
{
IPlan plan;
if (this.plans.TryGetValue(type, out plan))
{
return plan;
}
plan = this.CreateEmptyPlan(type);
this.plans.Add(type, plan);
this.Strategies.Map(s => s.Execute(plan));
return plan;
}
finally
{
this.plannerLock.DowngradeFromWriterLock(ref lockCooki);
}
}
public void RemovePlan(Type type)
{
plans.Remove(type);
plans.TrimExcess();
}
}

View file

@ -1,14 +1,13 @@
using DryIoc; using DryIoc;
using LinqToDB.Extensions;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NadekoBot.Modules.Music; using NadekoBot.Modules.Music;
using NadekoBot.Modules.Music.Resolvers; using NadekoBot.Modules.Music.Resolvers;
using NadekoBot.Modules.Music.Services; using NadekoBot.Modules.Music.Services;
using Ninject.Extensions.Conventions.Syntax;
using StackExchange.Redis; using StackExchange.Redis;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using Ninject.Infrastructure.Language;
namespace NadekoBot.Extensions; namespace NadekoBot.Extensions;
@ -32,63 +31,32 @@ public static class ServiceCollectionExtensions
return svcs; return svcs;
} }
public static IContainer AddConfigServices(this IContainer kernel, Assembly a) public static IContainer AddConfigServices(this IContainer svcs, Assembly a)
{ {
// kernel.RegisterMany([typeof(ConfigServiceBase<>)]);
foreach (var type in a.GetTypes() foreach (var type in a.GetTypes()
.Where(x => !x.IsAbstract && x.IsAssignableToGenericType(typeof(ConfigServiceBase<>)))) .Where(x => !x.IsAbstract && x.IsAssignableToGenericType(typeof(ConfigServiceBase<>))))
{ {
kernel.RegisterMany([type], svcs.RegisterMany([type],
getServiceTypes: type => type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType), getServiceTypes: type => type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType),
getImplFactory: type => ReflectionFactory.Of(type, Reuse.Singleton)); getImplFactory: type => ReflectionFactory.Of(type, Reuse.Singleton));
} }
// return svcs;
// kernel.Bind(x =>
// {
// var configs = x.From(a)
// .SelectAllClasses()
// .Where(f => f.IsAssignableToGenericType(typeof(ConfigServiceBase<>)));
//
// configs.BindToSelfWithInterfaces()
// .Configure(c => c.InSingletonScope());
// });
return kernel;
} }
public static IContainer AddConfigMigrators(this IContainer kernel, Assembly a)
=> kernel.AddSealedSubclassesOf(typeof(IConfigMigrator), a);
public static IContainer AddMusic(this IContainer kernel) public static IContainer AddMusic(this IContainer svcs)
{ {
kernel.RegisterMany<MusicService>(Reuse.Singleton); svcs.RegisterMany<MusicService>(Reuse.Singleton);
kernel.AddSingleton<ITrackResolveProvider, TrackResolveProvider>(); svcs.AddSingleton<ITrackResolveProvider, TrackResolveProvider>();
kernel.AddSingleton<IYoutubeResolver, YtdlYoutubeResolver>(); svcs.AddSingleton<IYoutubeResolver, YtdlYoutubeResolver>();
kernel.AddSingleton<ILocalTrackResolver, LocalTrackResolver>(); svcs.AddSingleton<ILocalTrackResolver, LocalTrackResolver>();
kernel.AddSingleton<IRadioResolver, RadioResolver>(); svcs.AddSingleton<IRadioResolver, RadioResolver>();
kernel.AddSingleton<ITrackCacher, TrackCacher>(); svcs.AddSingleton<ITrackCacher, TrackCacher>();
return kernel; return svcs;
}
public static IContainer AddSealedSubclassesOf(this IContainer cont, Type baseType, Assembly a)
{
var classes = a.GetExportedTypes()
.Where(x => x.IsClass && !x.IsAbstract && x.IsPublic)
.Where(x => x.IsNested && baseType.IsAssignableFrom(x));
foreach (var c in classes)
{
cont.RegisterMany([c], Reuse.Singleton);
// var inters = c.GetInterfaces();
// cont.RegisterMany(inters, c);
}
return cont;
} }
public static IContainer AddCache(this IContainer cont, IBotCredentials creds) public static IContainer AddCache(this IContainer cont, IBotCredentials creds)
@ -110,33 +78,31 @@ public static class ServiceCollectionExtensions
.AddBotStringsServices(creds.BotCache); .AddBotStringsServices(creds.BotCache);
} }
public static IContainer AddHttpClients(this IContainer kernel) public static IContainer AddHttpClients(this IContainer svcs)
{ {
IServiceCollection svcs = new ServiceCollection(); IServiceCollection proxySvcs = new ServiceCollection();
svcs.AddHttpClient(); proxySvcs.AddHttpClient();
svcs.AddHttpClient("memelist") proxySvcs.AddHttpClient("memelist")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{ {
AllowAutoRedirect = false AllowAutoRedirect = false
}); });
svcs.AddHttpClient("google:search") proxySvcs.AddHttpClient("google:search")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler()
{ {
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
}); });
var prov = svcs.BuildServiceProvider(); var prov = proxySvcs.BuildServiceProvider();
kernel.RegisterDelegate<IHttpClientFactory>(_ => prov.GetRequiredService<IHttpClientFactory>());
kernel.RegisterDelegate<HttpClient>(_ => prov.GetRequiredService<HttpClient>()); svcs.RegisterDelegate<IHttpClientFactory>(_ => prov.GetRequiredService<IHttpClientFactory>());
svcs.RegisterDelegate<HttpClient>(_ => prov.GetRequiredService<HttpClient>());
return kernel; return svcs;
} }
public static IConfigureSyntax BindToSelfWithInterfaces(this IJoinExcludeIncludeBindSyntax matcher) public static IContainer AddLifetimeServices(this IContainer svcs, Assembly a)
=> matcher.BindSelection((type, types) => types.Append(type));
public static IContainer AddLifetimeServices(this IContainer kernel, Assembly a)
{ {
Type[] types = Type[] types =
[ [
@ -149,27 +115,17 @@ public static class ServiceCollectionExtensions
]; ];
foreach (var svc in a.GetTypes() foreach (var svc in a.GetTypes()
.Where(type => type.IsClass && types.Any(t => type.IsAssignableTo(t)) && !type.HasAttribute<DIIgnoreAttribute>())) .Where(type => type.IsClass && types.Any(t => type.IsAssignableTo(t)) && !type.HasAttribute<DIIgnoreAttribute>()
#if GLOBAL_NADEKO
&& !type.HasAttribute<NoPublicBotAttribute>()
#endif
))
{ {
kernel.RegisterMany([svc], svcs.RegisterMany([svc],
getServiceTypes: type => type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType), getServiceTypes: type => type.GetImplementedTypes(ReflectionTools.AsImplementedType.SourceType),
getImplFactory: type => ReflectionFactory.Of(type, Reuse.Singleton)); getImplFactory: type => ReflectionFactory.Of(type, Reuse.Singleton));
} }
//
// kernel.RegisterMany(
// [a],
// #if GLOBAL_NADEKO
// && !c.HasAttribute<NoPublicBotAttribute>()
// #endif
// ),
// reuse:
// Reuse.Singleton
// );
return svcs;
// todo maybe self is missing
// todo maybe attribute doesn't work
return kernel;
} }
} }

View file

@ -1,7 +0,0 @@
#nullable disable
namespace NadekoBot.Services;
public interface IConfigMigrator
{
public void EnsureMigrated();
}