mirror of
https://github.com/Bubuni-Team/telegram-bot.git
synced 2026-07-31 00:29:24 +03:00
Use file-scoped namespaces
This commit is contained in:
+171
-172
@@ -7,201 +7,200 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Kruzya.TelegramBot.Core
|
||||
namespace Kruzya.TelegramBot.Core;
|
||||
|
||||
public sealed class Core
|
||||
{
|
||||
public sealed class Core
|
||||
{
|
||||
/// <summary>
|
||||
/// The Core API version.
|
||||
/// </summary>
|
||||
public UInt32 ApiVersion => 1;
|
||||
/// <summary>
|
||||
/// The Core API version.
|
||||
/// </summary>
|
||||
public UInt32 ApiVersion => 1;
|
||||
|
||||
/// <summary>
|
||||
/// The array that contains all modules instances.
|
||||
/// </summary>
|
||||
public Module[] Modules => _modules.ToArray();
|
||||
/// <summary>
|
||||
/// The array that contains all modules instances.
|
||||
/// </summary>
|
||||
public Module[] Modules => _modules.ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// The current bot configuration.
|
||||
/// </summary>
|
||||
public IConfiguration Configuration => _configuration;
|
||||
/// <summary>
|
||||
/// The current bot configuration.
|
||||
/// </summary>
|
||||
public IConfiguration Configuration => _configuration;
|
||||
|
||||
#region Private properties
|
||||
#region Private properties
|
||||
|
||||
/// <summary>
|
||||
/// The array that contains all modules instances.
|
||||
/// </summary>
|
||||
private List<Module> _modules = new List<Module>();
|
||||
/// <summary>
|
||||
/// The array that contains all modules instances.
|
||||
/// </summary>
|
||||
private List<Module> _modules = new List<Module>();
|
||||
|
||||
/// <summary>
|
||||
/// The current bot configuration.
|
||||
/// </summary>
|
||||
private IConfiguration _configuration;
|
||||
/// <summary>
|
||||
/// The current bot configuration.
|
||||
/// </summary>
|
||||
private IConfiguration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// The all directories for loading our module assemblies.
|
||||
/// </summary>
|
||||
private List<string> _directories
|
||||
/// <summary>
|
||||
/// The all directories for loading our module assemblies.
|
||||
/// </summary>
|
||||
private List<string> _directories
|
||||
{
|
||||
get
|
||||
{
|
||||
get
|
||||
// For start, load all assemblies in required directory.
|
||||
// And register in internal temporary array.
|
||||
var modulesDirectory = new List<string>(new string[]
|
||||
{
|
||||
// For start, load all assemblies in required directory.
|
||||
// And register in internal temporary array.
|
||||
var modulesDirectory = new List<string>(new string[]
|
||||
{
|
||||
Path.Join(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "modules"),
|
||||
Path.Join(Environment.CurrentDirectory, "modules")
|
||||
});
|
||||
Path.Join(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "modules"),
|
||||
Path.Join(Environment.CurrentDirectory, "modules")
|
||||
});
|
||||
|
||||
// Also lookup load directories from config.
|
||||
modulesDirectory.AddRange(_configuration.GetSection("ModuleDirectories").GetChildren().Select(q => q.Value)
|
||||
.ToList());
|
||||
// Also lookup load directories from config.
|
||||
modulesDirectory.AddRange(_configuration.GetSection("ModuleDirectories").GetChildren().Select(q => q.Value)
|
||||
.ToList());
|
||||
|
||||
return modulesDirectory;
|
||||
}
|
||||
return modulesDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of filenames (without extension) what should be not loaded.
|
||||
/// </summary>
|
||||
private List<string> _ignoredAssemblyFilenames => _configuration
|
||||
.GetSection("IgnoredModuleFilenames").GetChildren()
|
||||
.Select(q => q.Value).ToList();
|
||||
/// <summary>
|
||||
/// List of filenames (without extension) what should be not loaded.
|
||||
/// </summary>
|
||||
private List<string> _ignoredAssemblyFilenames => _configuration
|
||||
.GetSection("IgnoredModuleFilenames").GetChildren()
|
||||
.Select(q => q.Value).ToList();
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
public Core(IConfiguration configuration)
|
||||
public Core(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
|
||||
HookAppDomain();
|
||||
Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes all required submodules.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
var types = new List<Type>();
|
||||
foreach (var directory in _directories)
|
||||
{
|
||||
_configuration = configuration;
|
||||
|
||||
HookAppDomain();
|
||||
Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes all required submodules.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
var types = new List<Type>();
|
||||
foreach (var directory in _directories)
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories))
|
||||
{
|
||||
var assemblyTypes = LoadAssembly(file);
|
||||
if (assemblyTypes == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories))
|
||||
{
|
||||
var assemblyTypes = LoadAssembly(file);
|
||||
if (assemblyTypes == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
types.AddRange(assemblyTypes);
|
||||
}
|
||||
types.AddRange(assemblyTypes);
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate them.
|
||||
foreach (var module in types)
|
||||
{
|
||||
EnsureLoaded(module);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the assembly and appends all Module types to list.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path to assembly file</param>
|
||||
/// <returns><see cref="IEnumerable{T}"/> if success, null otherwise.</returns>
|
||||
private IEnumerable<Type>? LoadAssembly(string path)
|
||||
// Instantiate them.
|
||||
foreach (var module in types)
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(path).ToString();
|
||||
if (_ignoredAssemblyFilenames.Contains(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Assembly assembly;
|
||||
try
|
||||
{
|
||||
assembly = Assembly.LoadFile(path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// don't load any invalid module.
|
||||
return null;
|
||||
}
|
||||
|
||||
return assembly.GetExportedTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Module)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures if module is loaded and started. Note that call can change module event chain.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Module type.</typeparam>
|
||||
/// <returns>Module instance.</returns>
|
||||
public T EnsureLoaded<T>()
|
||||
where T : Module
|
||||
{
|
||||
return (T) EnsureLoaded(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures if module is loaded and started. Note that call can change module event chain.
|
||||
/// </summary>
|
||||
/// <param name="moduleType">The Module type.</param>
|
||||
/// <returns>Module instance.</returns>
|
||||
/// <exception cref="Exception">The given type is not extends the Module class</exception>
|
||||
public Module EnsureLoaded(Type moduleType)
|
||||
{
|
||||
if (!moduleType.IsSubclassOf(typeof(Module)))
|
||||
{
|
||||
throw new Exception("Received incorrect module type");
|
||||
}
|
||||
|
||||
var module = _modules.FirstOrDefault(module => module.GetType() == moduleType);
|
||||
if (module == null)
|
||||
{
|
||||
module = (Module) Activator.CreateInstance(moduleType, this)!;
|
||||
Console.WriteLine($"Started module {module.GetType().Name}");
|
||||
_modules.Add(module);
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
private void HookAppDomain()
|
||||
{
|
||||
var appDomain = AppDomain.CurrentDomain;
|
||||
|
||||
appDomain.AssemblyResolve += delegate(object? sender, ResolveEventArgs args)
|
||||
{
|
||||
var assemblyName = new AssemblyName(args.Name).Name + ".dll";
|
||||
|
||||
var possiblePaths = new List<string>();
|
||||
if (args.RequestingAssembly != null)
|
||||
{
|
||||
var modulePath = Path.GetDirectoryName(args.RequestingAssembly.Location)!;
|
||||
possiblePaths.Add(modulePath);
|
||||
possiblePaths.Add(args.RequestingAssembly.Location.Replace(".dll", ""));
|
||||
}
|
||||
|
||||
possiblePaths.Add(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!);
|
||||
possiblePaths.Add(Environment.CurrentDirectory);
|
||||
|
||||
foreach (var basePath in possiblePaths)
|
||||
{
|
||||
var assemblyPath = $"{basePath}/{assemblyName}";
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
return Assembly.LoadFrom(assemblyPath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
EnsureLoaded(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the assembly and appends all Module types to list.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path to assembly file</param>
|
||||
/// <returns><see cref="IEnumerable{T}"/> if success, null otherwise.</returns>
|
||||
private IEnumerable<Type>? LoadAssembly(string path)
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(path).ToString();
|
||||
if (_ignoredAssemblyFilenames.Contains(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Assembly assembly;
|
||||
try
|
||||
{
|
||||
assembly = Assembly.LoadFile(path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// don't load any invalid module.
|
||||
return null;
|
||||
}
|
||||
|
||||
return assembly.GetExportedTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Module)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures if module is loaded and started. Note that call can change module event chain.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Module type.</typeparam>
|
||||
/// <returns>Module instance.</returns>
|
||||
public T EnsureLoaded<T>()
|
||||
where T : Module
|
||||
{
|
||||
return (T) EnsureLoaded(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures if module is loaded and started. Note that call can change module event chain.
|
||||
/// </summary>
|
||||
/// <param name="moduleType">The Module type.</param>
|
||||
/// <returns>Module instance.</returns>
|
||||
/// <exception cref="Exception">The given type is not extends the Module class</exception>
|
||||
public Module EnsureLoaded(Type moduleType)
|
||||
{
|
||||
if (!moduleType.IsSubclassOf(typeof(Module)))
|
||||
{
|
||||
throw new Exception("Received incorrect module type");
|
||||
}
|
||||
|
||||
var module = _modules.FirstOrDefault(module => module.GetType() == moduleType);
|
||||
if (module == null)
|
||||
{
|
||||
module = (Module) Activator.CreateInstance(moduleType, this)!;
|
||||
Console.WriteLine($"Started module {module.GetType().Name}");
|
||||
_modules.Add(module);
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
private void HookAppDomain()
|
||||
{
|
||||
var appDomain = AppDomain.CurrentDomain;
|
||||
|
||||
appDomain.AssemblyResolve += delegate(object? sender, ResolveEventArgs args)
|
||||
{
|
||||
var assemblyName = new AssemblyName(args.Name).Name + ".dll";
|
||||
|
||||
var possiblePaths = new List<string>();
|
||||
if (args.RequestingAssembly != null)
|
||||
{
|
||||
var modulePath = Path.GetDirectoryName(args.RequestingAssembly.Location)!;
|
||||
possiblePaths.Add(modulePath);
|
||||
possiblePaths.Add(args.RequestingAssembly.Location.Replace(".dll", ""));
|
||||
}
|
||||
|
||||
possiblePaths.Add(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!);
|
||||
possiblePaths.Add(Environment.CurrentDirectory);
|
||||
|
||||
foreach (var basePath in possiblePaths)
|
||||
{
|
||||
var assemblyPath = $"{basePath}/{assemblyName}";
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
return Assembly.LoadFrom(assemblyPath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user