mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	More cleanup, namespace fixes, convenience methods for IKernel
This commit is contained in:
		@@ -44,7 +44,7 @@ public sealed class Bot : IBot
 | 
			
		||||
 | 
			
		||||
    private readonly IBotCredsProvider _credsProvider;
 | 
			
		||||
 | 
			
		||||
    private readonly Assembly[] _moduleAssemblies;
 | 
			
		||||
    private readonly Assembly[] _loadedAssemblies;
 | 
			
		||||
    // private readonly InteractionService _interactionService;
 | 
			
		||||
 | 
			
		||||
    public Bot(int shardId, int? totalShards, string credPath = null)
 | 
			
		||||
@@ -95,22 +95,15 @@ public sealed class Bot : IBot
 | 
			
		||||
        // _interactionService = new(Client.Rest);
 | 
			
		||||
 | 
			
		||||
        Client.Log += Client_Log;
 | 
			
		||||
        _moduleAssemblies = new[]
 | 
			
		||||
        _loadedAssemblies = new[]
 | 
			
		||||
        {
 | 
			
		||||
            typeof(Bot).Assembly, // bot
 | 
			
		||||
            typeof(Creds).Assembly, // bot.common
 | 
			
		||||
 | 
			
		||||
            // modules
 | 
			
		||||
            typeof(NadekoExpressions).Assembly,
 | 
			
		||||
            typeof(Administration).Assembly,
 | 
			
		||||
            typeof(Gambling).Assembly,
 | 
			
		||||
            typeof(Help).Assembly,
 | 
			
		||||
            typeof(Music).Assembly,
 | 
			
		||||
            typeof(Patronage).Assembly,
 | 
			
		||||
            typeof(Permissions).Assembly,
 | 
			
		||||
            typeof(Searches).Assembly,
 | 
			
		||||
            typeof(Utility).Assembly,
 | 
			
		||||
            typeof(Xp).Assembly,
 | 
			
		||||
            typeof(NadekoExpressions).Assembly, typeof(Administration).Assembly, typeof(Gambling).Assembly,
 | 
			
		||||
            typeof(Help).Assembly, typeof(Music).Assembly, typeof(Patronage).Assembly, typeof(Permissions).Assembly,
 | 
			
		||||
            typeof(Searches).Assembly, typeof(Utility).Assembly, typeof(Xp).Assembly,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -130,59 +123,62 @@ public sealed class Bot : IBot
 | 
			
		||||
            AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var kernel = new StandardKernel(new NinjectSettings()
 | 
			
		||||
        var svcs = new StandardKernel(new NinjectSettings()
 | 
			
		||||
        {
 | 
			
		||||
            ThrowOnGetServiceNotFound = true,
 | 
			
		||||
            ActivationCacheDisabled = true,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        kernel.Components.Remove<IPlanner, Planner>();
 | 
			
		||||
        kernel.Components.Add<IPlanner, RemovablePlanner>();
 | 
			
		||||
        // this is required in order for medusa unloading to work
 | 
			
		||||
        svcs.Components.Remove<IPlanner, Planner>();
 | 
			
		||||
        svcs.Components.Add<IPlanner, RemovablePlanner>();
 | 
			
		||||
 | 
			
		||||
        kernel.Bind<IBotCredentials>().ToMethod(_ => _credsProvider.GetCreds()).InTransientScope();
 | 
			
		||||
        svcs.AddSingleton<IBotCredentials, IBotCredentials>(_ => _credsProvider.GetCreds());
 | 
			
		||||
        svcs.AddSingleton<DbService, DbService>(_db);
 | 
			
		||||
        svcs.AddSingleton<IBotCredsProvider>(_credsProvider);
 | 
			
		||||
        svcs.AddSingleton<DiscordSocketClient>(Client);
 | 
			
		||||
        svcs.AddSingleton<CommandService>(_commandService);
 | 
			
		||||
        svcs.AddSingleton<Bot>(this);
 | 
			
		||||
        svcs.AddSingleton<IBot>(this);
 | 
			
		||||
 | 
			
		||||
        kernel.Bind<IBotCredsProvider>().ToConstant(_credsProvider).InSingletonScope();
 | 
			
		||||
        kernel.Bind<DbService>().ToConstant(_db).InSingletonScope();
 | 
			
		||||
        kernel.Bind<DiscordSocketClient>().ToConstant(Client).InSingletonScope();
 | 
			
		||||
        kernel.Bind<CommandService>().ToConstant(_commandService).InSingletonScope();
 | 
			
		||||
        kernel.Bind<Bot>().ToConstant(this).InSingletonScope();
 | 
			
		||||
        kernel.Bind<IBot>().ToConstant(this).InSingletonScope();
 | 
			
		||||
 | 
			
		||||
        kernel.Bind<ISeria>().To<JsonSeria>().InSingletonScope();
 | 
			
		||||
        kernel.Bind<IConfigSeria>().To<YamlSeria>().InSingletonScope();
 | 
			
		||||
        kernel.Bind<IMemoryCache>().ToConstant(new MemoryCache(new MemoryCacheOptions())).InSingletonScope();
 | 
			
		||||
        svcs.AddSingleton<ISeria, JsonSeria>();
 | 
			
		||||
        svcs.AddSingleton<IConfigSeria, YamlSeria>();
 | 
			
		||||
        svcs.AddSingleton<IMemoryCache, MemoryCache>(new MemoryCache(new MemoryCacheOptions()));
 | 
			
		||||
        svcs.AddSingleton<IBehaviorHandler, BehaviorHandler>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        foreach (var a in _moduleAssemblies)
 | 
			
		||||
        foreach (var a in _loadedAssemblies)
 | 
			
		||||
        {
 | 
			
		||||
            kernel.AddConfigServices(a)
 | 
			
		||||
                  .AddConfigMigrators(a)
 | 
			
		||||
                  .AddLifetimeServices(a);
 | 
			
		||||
            svcs.AddConfigServices(a)
 | 
			
		||||
                .AddConfigMigrators(a)
 | 
			
		||||
                .AddLifetimeServices(a);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        kernel.AddMusic()
 | 
			
		||||
              .AddCache(_creds)
 | 
			
		||||
              .AddHttpClients();
 | 
			
		||||
        svcs.AddMusic()
 | 
			
		||||
            .AddCache(_creds)
 | 
			
		||||
            .AddHttpClients();
 | 
			
		||||
 | 
			
		||||
        if (Environment.GetEnvironmentVariable("NADEKOBOT_IS_COORDINATED") != "1")
 | 
			
		||||
        {
 | 
			
		||||
            kernel.Bind<ICoordinator>().To<SingleProcessCoordinator>().InSingletonScope();
 | 
			
		||||
            svcs.AddSingleton<ICoordinator, SingleProcessCoordinator>();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            kernel.Bind<ICoordinator, IReadyExecutor>().To<RemoteGrpcCoordinator>().InSingletonScope();
 | 
			
		||||
            svcs.AddSingleton<RemoteGrpcCoordinator>();
 | 
			
		||||
            svcs.AddSingleton<ICoordinator>(_ => svcs.GetRequiredService<RemoteGrpcCoordinator>());
 | 
			
		||||
            svcs.AddSingleton<IReadyExecutor>(_ => svcs.GetRequiredService<RemoteGrpcCoordinator>());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        kernel.Bind<IServiceProvider>().ToConstant(kernel).InSingletonScope();
 | 
			
		||||
        svcs.AddSingleton<IServiceProvider>(svcs);
 | 
			
		||||
 | 
			
		||||
        //initialize Services
 | 
			
		||||
        Services = kernel;
 | 
			
		||||
        Services = svcs;
 | 
			
		||||
        Services.GetRequiredService<IBehaviorHandler>().Initialize();
 | 
			
		||||
 | 
			
		||||
        if (Client.ShardId == 0)
 | 
			
		||||
            ApplyConfigMigrations();
 | 
			
		||||
 | 
			
		||||
        foreach (var a in _moduleAssemblies)
 | 
			
		||||
        foreach (var a in _loadedAssemblies)
 | 
			
		||||
        {
 | 
			
		||||
            LoadTypeReaders(a);
 | 
			
		||||
        }
 | 
			
		||||
@@ -316,7 +312,7 @@ public sealed class Bot : IBot
 | 
			
		||||
        // start handling messages received in commandhandler
 | 
			
		||||
        await commandHandler.StartHandling();
 | 
			
		||||
 | 
			
		||||
        foreach (var a in _moduleAssemblies)
 | 
			
		||||
        foreach (var a in _loadedAssemblies)
 | 
			
		||||
        {
 | 
			
		||||
            await _commandService.AddModulesAsync(a, Services);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								src/NadekoBot/Common/NinjectIKernelExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/NadekoBot/Common/NinjectIKernelExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
using Ninject;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Extensions;
 | 
			
		||||
 | 
			
		||||
public static class NinjectIKernelExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static IKernel AddSingleton<TImpl>(this IKernel kernel)
 | 
			
		||||
    {
 | 
			
		||||
        kernel.Bind<TImpl>().ToSelf().InSingletonScope();
 | 
			
		||||
        return kernel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IKernel AddSingleton<TInterface, TImpl>(this IKernel kernel)
 | 
			
		||||
        where TImpl : TInterface
 | 
			
		||||
    {
 | 
			
		||||
        kernel.Bind<TInterface>().To<TImpl>().InSingletonScope();
 | 
			
		||||
        return kernel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IKernel AddSingleton<TImpl>(this IKernel kernel, TImpl obj)
 | 
			
		||||
        => kernel.AddSingleton<TImpl, TImpl>(obj);
 | 
			
		||||
 | 
			
		||||
    public static IKernel AddSingleton<TInterface, TImpl>(this IKernel kernel, TImpl obj)
 | 
			
		||||
        where TImpl : TInterface
 | 
			
		||||
    {
 | 
			
		||||
        kernel.Bind<TInterface>().ToConstant(obj).InSingletonScope();
 | 
			
		||||
        return kernel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IKernel AddSingleton<TImpl, TInterface>(
 | 
			
		||||
        this IKernel kernel,
 | 
			
		||||
        Func<Ninject.Activation.IContext, TImpl> factory)
 | 
			
		||||
        where TImpl : TInterface
 | 
			
		||||
    {
 | 
			
		||||
        kernel.Bind<TInterface>().ToMethod(factory).InSingletonScope();
 | 
			
		||||
        return kernel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IKernel AddSingleton<TImpl>(
 | 
			
		||||
        this IKernel kernel,
 | 
			
		||||
        Func<Ninject.Activation.IContext, TImpl> factory)
 | 
			
		||||
        => kernel.AddSingleton<TImpl, TImpl>(factory);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using NadekoBot.Modules.Music;
 | 
			
		||||
using NadekoBot.Modules.Music.Resolvers;
 | 
			
		||||
using NadekoBot.Modules.Music.Services;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
using NadekoBot.Common;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
[DIIgnore]
 | 
			
		||||
public sealed class BehaviorAdapter : ICustomBehavior
 | 
			
		||||
{
 | 
			
		||||
@@ -12,6 +8,8 @@ public sealed class BehaviorAdapter : ICustomBehavior
 | 
			
		||||
    private readonly IServiceProvider _services;
 | 
			
		||||
    private readonly string _name;
 | 
			
		||||
 | 
			
		||||
    public string Name => _name;
 | 
			
		||||
 | 
			
		||||
    // unused
 | 
			
		||||
    public int Priority
 | 
			
		||||
        => 0;
 | 
			
		||||
@@ -53,7 +51,7 @@ public sealed class BehaviorAdapter : ICustomBehavior
 | 
			
		||||
    {
 | 
			
		||||
        if (!_snekWr.TryGetTarget(out var snek))
 | 
			
		||||
            return null;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return await snek.ExecInputTransformAsync(guild, channel, user, input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +67,7 @@ public sealed class BehaviorAdapter : ICustomBehavior
 | 
			
		||||
    {
 | 
			
		||||
        if (!_snekWr.TryGetTarget(out var snek))
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        await snek.ExecPostCommandAsync(ContextAdapterFactory.CreateNew(context, _strings, _services),
 | 
			
		||||
            moduleName,
 | 
			
		||||
            commandName);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,4 @@
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
internal class ContextAdapterFactory
 | 
			
		||||
internal class ContextAdapterFactory
 | 
			
		||||
{
 | 
			
		||||
    public static AnyContext CreateNew(ICommandContext context, IMedusaStrings strings, IServiceProvider services)
 | 
			
		||||
        => context.Guild is null
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,4 @@
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
using NadekoBot;
 | 
			
		||||
using NadekoBot.Common;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
public sealed class DmContextAdapter : DmContext
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
using Microsoft.VisualBasic;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using CommandStrings = Nadeko.Snake.CommandStrings;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Medusa;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,4 @@
 | 
			
		||||
using NadekoBot.Modules;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules;
 | 
			
		||||
namespace NadekoBot.Modules;
 | 
			
		||||
 | 
			
		||||
public interface IMedusaeRepositoryService
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
using Nadeko.Common;
 | 
			
		||||
using Nadeko.Medusa;
 | 
			
		||||
using Nadeko.Medusa;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,97 +24,97 @@
 | 
			
		||||
            <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
            <Publish>True</Publish>
 | 
			
		||||
        </PackageReference>
 | 
			
		||||
        <PackageReference Include="AWSSDK.S3" Version="3.7.101.58"/>
 | 
			
		||||
        <PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/>
 | 
			
		||||
        <PackageReference Include="CommandLineParser" Version="2.9.1"/>
 | 
			
		||||
        <PackageReference Include="CoreCLR-NCalc" Version="2.2.110"/>
 | 
			
		||||
        <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/>
 | 
			
		||||
        <PackageReference Include="Google.Apis.YouTube.v3" Version="1.60.0.2945"/>
 | 
			
		||||
        <PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084"/>
 | 
			
		||||
        <PackageReference Include="Google.Protobuf" Version="3.22.1"/>
 | 
			
		||||
        <PackageReference Include="Grpc.Net.ClientFactory" Version="2.52.0"/>
 | 
			
		||||
        <PackageReference Include="AWSSDK.S3" Version="3.7.101.58" />
 | 
			
		||||
        <PackageReference Include="CodeHollow.FeedReader" Version="1.2.6" />
 | 
			
		||||
        <PackageReference Include="CommandLineParser" Version="2.9.1" />
 | 
			
		||||
        <PackageReference Include="CoreCLR-NCalc" Version="2.2.110" />
 | 
			
		||||
        <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
 | 
			
		||||
        <PackageReference Include="Google.Apis.YouTube.v3" Version="1.60.0.2945" />
 | 
			
		||||
        <PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
 | 
			
		||||
        <PackageReference Include="Google.Protobuf" Version="3.22.1" />
 | 
			
		||||
        <PackageReference Include="Grpc.Net.ClientFactory" Version="2.52.0" />
 | 
			
		||||
        <PackageReference Include="Grpc.Tools" Version="2.53.0">
 | 
			
		||||
            <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
        </PackageReference>
 | 
			
		||||
        <PackageReference Include="Html2Markdown" Version="5.1.0.703"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0"/>
 | 
			
		||||
        <PackageReference Include="Html2Markdown" Version="5.1.0.703" />
 | 
			
		||||
        <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0" />
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="MorseCode.ITask" Version="2.0.3"/>
 | 
			
		||||
        <PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.0.0"/>
 | 
			
		||||
        <PackageReference Include="MorseCode.ITask" Version="2.0.3" />
 | 
			
		||||
        <PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.0.0" />
 | 
			
		||||
 | 
			
		||||
        <!-- DI -->
 | 
			
		||||
        <PackageReference Include="Ninject" Version="3.3.6"/>
 | 
			
		||||
        <PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0"/>
 | 
			
		||||
        <PackageReference Include="Ninject" Version="3.3.6" />
 | 
			
		||||
        <PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0" />
 | 
			
		||||
        <!--        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />-->
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
 | 
			
		||||
        <!--        <PackageReference Include="Scrutor" Version="4.2.0" />-->
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2"/>
 | 
			
		||||
        <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
 | 
			
		||||
        <PackageReference Include="NonBlocking" Version="2.1.1"/>
 | 
			
		||||
        <PackageReference Include="OneOf" Version="3.0.243"/>
 | 
			
		||||
        <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0"/>
 | 
			
		||||
        <PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.ImageSharp" Version="2.1.3"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009"/>
 | 
			
		||||
        <PackageReference Include="StackExchange.Redis" Version="2.6.104"/>
 | 
			
		||||
        <PackageReference Include="YamlDotNet" Version="13.0.2"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
 | 
			
		||||
        <PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
 | 
			
		||||
        <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
 | 
			
		||||
        <PackageReference Include="NonBlocking" Version="2.1.1" />
 | 
			
		||||
        <PackageReference Include="OneOf" Version="3.0.243" />
 | 
			
		||||
        <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
 | 
			
		||||
        <PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
 | 
			
		||||
        <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17" />
 | 
			
		||||
        <PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
 | 
			
		||||
        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
 | 
			
		||||
        <PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009" />
 | 
			
		||||
        <PackageReference Include="StackExchange.Redis" Version="2.6.104" />
 | 
			
		||||
        <PackageReference Include="YamlDotNet" Version="13.0.2" />
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="Humanizer" Version="2.14.1">
 | 
			
		||||
            <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
            <Publish>True</Publish>
 | 
			
		||||
        </PackageReference>
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="JetBrains.Annotations" Version="2022.3.1"/>
 | 
			
		||||
        <PackageReference Include="JetBrains.Annotations" Version="2022.3.1" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        <!-- Db-related packages -->
 | 
			
		||||
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
 | 
			
		||||
        <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4">
 | 
			
		||||
            <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
        </PackageReference>
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="linq2db.EntityFrameworkCore" Version="7.3.0"/>
 | 
			
		||||
        <PackageReference Include="linq2db.EntityFrameworkCore" Version="7.3.0" />
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4"/>
 | 
			
		||||
        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.3"/>
 | 
			
		||||
        <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4" />
 | 
			
		||||
        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.3" />
 | 
			
		||||
        <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="EFCore.NamingConventions" Version="7.0.2"/>
 | 
			
		||||
        <PackageReference Include="EFCore.NamingConventions" Version="7.0.2" />
 | 
			
		||||
 | 
			
		||||
        <!-- Used by stream notifications -->
 | 
			
		||||
        <PackageReference Include="TwitchLib.Api" Version="3.4.1"/>
 | 
			
		||||
        <PackageReference Include="TwitchLib.Api" Version="3.4.1" />
 | 
			
		||||
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
        <ProjectReference Include="..\ayu\Ayu.Discord.Voice\Ayu.Discord.Voice.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Econ\Nadeko.Econ.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Medusa\Nadeko.Medusa.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Common\Nadeko.Bot.Common.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Administration\Nadeko.Bot.Modules.Administration.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Help\Nadeko.Bot.Modules.Help.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Music\Nadeko.Bot.Modules.Music.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Patronage\Nadeko.Bot.Modules.Patronage.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Permisssions\Nadeko.Bot.Modules.Permisssions.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Utility\Nadeko.Bot.Modules.Utility.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Xp\Nadeko.Bot.Modules.Xp.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\ayu\Ayu.Discord.Voice\Ayu.Discord.Voice.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Econ\Nadeko.Econ.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Medusa\Nadeko.Medusa.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Common\Nadeko.Bot.Common.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Administration\Nadeko.Bot.Modules.Administration.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Help\Nadeko.Bot.Modules.Help.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Music\Nadeko.Bot.Modules.Music.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Patronage\Nadeko.Bot.Modules.Patronage.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Permisssions\Nadeko.Bot.Modules.Permisssions.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Utility\Nadeko.Bot.Modules.Utility.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Xp\Nadeko.Bot.Modules.Xp.csproj" />
 | 
			
		||||
        <!--        <ProjectReference Include="..\Nadeko.Common\Nadeko.Common.csproj" />-->
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Expresssions\Nadeko.Bot.Modules.Expresssions.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Gambling\Nadeko.Bot.Modules.Gambling.csproj"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Expresssions\Nadeko.Bot.Modules.Expresssions.csproj" />
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Modules.Gambling\Nadeko.Bot.Modules.Gambling.csproj" />
 | 
			
		||||
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Generators.Cloneable\Nadeko.Bot.Generators.Cloneable.csproj" OutputItemType="Analyzer"/>
 | 
			
		||||
        <ProjectReference Include="..\Nadeko.Bot.Generators.Cloneable\Nadeko.Bot.Generators.Cloneable.csproj" OutputItemType="Analyzer" />
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
        <Protobuf Include="..\NadekoBot.Coordinator\Protos\coordinator.proto" GrpcServices="Client">
 | 
			
		||||
@@ -130,9 +130,6 @@
 | 
			
		||||
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
 | 
			
		||||
        </None>
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
        <Folder Include="Common\TypeReaders\"/>
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
 | 
			
		||||
    <PropertyGroup Condition=" '$(Version)' == '' ">
 | 
			
		||||
        <VersionPrefix Condition=" '$(VersionPrefix)' == '' ">5.0.0</VersionPrefix>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
using Nadeko.Common;
 | 
			
		||||
 | 
			
		||||
var pid = Environment.ProcessId;
 | 
			
		||||
 | 
			
		||||
var shardId = 0;
 | 
			
		||||
@@ -25,6 +23,6 @@ if (args.Length > 0 && args[0] != "run")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LogSetup.SetupLogger(shardId);
 | 
			
		||||
Log.Information("Pid: {ProcessId}", pid);
 | 
			
		||||
 | 
			
		||||
Log.Information("Pid: {ProcessId}", pid);
 | 
			
		||||
 | 
			
		||||
await new Bot(shardId, totalShards, Environment.GetEnvironmentVariable("NadekoBot__creds")).RunAndBlockAsync();
 | 
			
		||||
@@ -1,305 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
using Ninject;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
// should be renamed to handler as it's not only executing
 | 
			
		||||
public sealed class BehaviorHandler : IBehaviorHandler, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceProvider _services;
 | 
			
		||||
    
 | 
			
		||||
    private IReadOnlyCollection<IExecNoCommand> noCommandExecs;
 | 
			
		||||
    private IReadOnlyCollection<IExecPreCommand> preCommandExecs;
 | 
			
		||||
    private IReadOnlyCollection<IExecOnMessage> onMessageExecs;
 | 
			
		||||
    private IReadOnlyCollection<IInputTransformer> inputTransformers;
 | 
			
		||||
 | 
			
		||||
    private readonly SemaphoreSlim _customLock = new(1, 1);
 | 
			
		||||
    private readonly List<ICustomBehavior> _customExecs = new();
 | 
			
		||||
 | 
			
		||||
    public BehaviorHandler(IServiceProvider services)
 | 
			
		||||
    {
 | 
			
		||||
        _services = services;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Initialize()
 | 
			
		||||
    {
 | 
			
		||||
        noCommandExecs = _services.GetServices<IExecNoCommand>().ToArray();
 | 
			
		||||
        preCommandExecs = _services.GetServices<IExecPreCommand>().OrderByDescending(x => x.Priority).ToArray();
 | 
			
		||||
        onMessageExecs = _services.GetServices<IExecOnMessage>().OrderByDescending(x => x.Priority).ToArray();
 | 
			
		||||
        inputTransformers = _services.GetServices<IInputTransformer>().ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region Add/Remove
 | 
			
		||||
 | 
			
		||||
    public async Task AddRangeAsync(IEnumerable<ICustomBehavior> execs)
 | 
			
		||||
    {
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var exe in execs)
 | 
			
		||||
            {
 | 
			
		||||
                if (_customExecs.Contains(exe))
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                _customExecs.Add(exe);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public async Task<bool> AddAsync(ICustomBehavior behavior)
 | 
			
		||||
    {
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (_customExecs.Contains(behavior))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            _customExecs.Add(behavior);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public async Task<bool> RemoveAsync(ICustomBehavior behavior)
 | 
			
		||||
    {
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            return _customExecs.Remove(behavior);
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public async Task RemoveRangeAsync(IEnumerable<ICustomBehavior> behs)
 | 
			
		||||
    {
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            foreach(var beh in behs)
 | 
			
		||||
                _customExecs.Remove(beh);
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
    
 | 
			
		||||
    #region Running
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> RunExecOnMessageAsync(SocketGuild guild, IUserMessage usrMsg)
 | 
			
		||||
    {
 | 
			
		||||
        async Task<bool> Exec<T>(IReadOnlyCollection<T> execs)
 | 
			
		||||
            where T : IExecOnMessage
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var exec in execs)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (await exec.ExecOnMessageAsync(guild, usrMsg))
 | 
			
		||||
                    {
 | 
			
		||||
                        Log.Information("{TypeName} blocked message g:{GuildId} u:{UserId} c:{ChannelId} msg:{Message}",
 | 
			
		||||
                            GetExecName(exec),
 | 
			
		||||
                            guild?.Id,
 | 
			
		||||
                            usrMsg.Author.Id,
 | 
			
		||||
                            usrMsg.Channel.Id,
 | 
			
		||||
                            usrMsg.Content?.TrimTo(10));
 | 
			
		||||
                        
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    Log.Error(ex,
 | 
			
		||||
                        "An error occurred in {TypeName} late blocker: {ErrorMessage}",
 | 
			
		||||
                        GetExecName(exec),
 | 
			
		||||
                        ex.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (await Exec(onMessageExecs))
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (await Exec(_customExecs))
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private string GetExecName(object exec)
 | 
			
		||||
        => exec is BehaviorAdapter ba
 | 
			
		||||
            ? ba.ToString()
 | 
			
		||||
            : exec.GetType().Name;
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> RunPreCommandAsync(ICommandContext ctx, CommandInfo cmd)
 | 
			
		||||
    {
 | 
			
		||||
        async Task<bool> Exec<T>(IReadOnlyCollection<T> execs) where T: IExecPreCommand
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var exec in execs)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    if (await exec.ExecPreCommandAsync(ctx, cmd.Module.GetTopLevelModule().Name, cmd))
 | 
			
		||||
                    {
 | 
			
		||||
                        Log.Information("{TypeName} Pre-Command blocked [{User}] Command: [{Command}]",
 | 
			
		||||
                            GetExecName(exec),
 | 
			
		||||
                            ctx.User,
 | 
			
		||||
                            cmd.Aliases[0]);
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    Log.Error(ex,
 | 
			
		||||
                        "An error occurred in {TypeName} PreCommand: {ErrorMessage}",
 | 
			
		||||
                        GetExecName(exec),
 | 
			
		||||
                        ex.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (await Exec(preCommandExecs))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (await Exec(_customExecs))
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task RunOnNoCommandAsync(SocketGuild guild, IUserMessage usrMsg)
 | 
			
		||||
    {
 | 
			
		||||
        async Task Exec<T>(IReadOnlyCollection<T> execs) where T : IExecNoCommand
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var exec in execs)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await exec.ExecOnNoCommandAsync(guild, usrMsg);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    Log.Error(ex,
 | 
			
		||||
                        "An error occurred in {TypeName} OnNoCommand: {ErrorMessage}",
 | 
			
		||||
                        GetExecName(exec),
 | 
			
		||||
                        ex.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Exec(noCommandExecs);
 | 
			
		||||
        
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await Exec(_customExecs);
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<string> RunInputTransformersAsync(SocketGuild guild, IUserMessage usrMsg)
 | 
			
		||||
    {
 | 
			
		||||
        async Task<string> Exec<T>(IReadOnlyCollection<T> execs, string content)
 | 
			
		||||
            where T : IInputTransformer
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var exec in execs)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var newContent = await exec.TransformInput(guild, usrMsg.Channel, usrMsg.Author, content);
 | 
			
		||||
                    if (newContent is not null)
 | 
			
		||||
                    {
 | 
			
		||||
                        Log.Information("{ExecName} transformed content {OldContent} -> {NewContent}",
 | 
			
		||||
                            GetExecName(exec),
 | 
			
		||||
                            content,
 | 
			
		||||
                            newContent);
 | 
			
		||||
                        return newContent;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    Log.Warning(ex, "An error occured during InputTransform handling: {ErrorMessage}", ex.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var newContent = await Exec(inputTransformers, usrMsg.Content);
 | 
			
		||||
        if (newContent is not null)
 | 
			
		||||
            return newContent;
 | 
			
		||||
        
 | 
			
		||||
        await _customLock.WaitAsync();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            newContent = await Exec(_customExecs, usrMsg.Content);
 | 
			
		||||
            if (newContent is not null)
 | 
			
		||||
                return newContent;
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _customLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return usrMsg.Content;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async ValueTask RunPostCommandAsync(ICommandContext ctx, string moduleName, CommandInfo cmd)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var exec in _customExecs)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await exec.ExecPostCommandAsync(ctx, moduleName, cmd.Name);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Log.Warning(ex,
 | 
			
		||||
                    "An error occured during PostCommand handling in {ExecName}: {ErrorMessage}",
 | 
			
		||||
                    GetExecName(exec),
 | 
			
		||||
                    ex.Message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #endregion
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using StackExchange.Redis;
 | 
			
		||||
using System.Web;
 | 
			
		||||
using Nadeko.Common;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Grpc.Core;
 | 
			
		||||
using Grpc.Net.Client;
 | 
			
		||||
using Nadeko.Common;
 | 
			
		||||
using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
using NadekoBot.Coordinator;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user