Files
telegram-bot/Core/Core.cs
T

196 lines
6.4 KiB
C#
Raw Normal View History

2022-01-22 17:13:38 +03:00
#nullable enable
using System;
2020-03-01 22:09:14 +04:00
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Configuration;
2020-03-01 22:46:07 +04:00
using Microsoft.Extensions.Logging;
2020-03-01 22:09:14 +04:00
namespace Kruzya.TelegramBot.Core
{
public sealed class Core
{
/// <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 current bot configuration.
/// </summary>
public IConfiguration Configuration => _configuration;
#region Private properties
/// <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 all directories for loading our module assemblies.
/// </summary>
private List<string> _directories
{
get
{
// For start, load all assemblies in required directory.
// And register in internal temporary array.
var modulesDirectory = new List<string>(new string[]
{
2020-03-01 22:46:07 +04:00
Path.Join(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "modules"),
2020-03-01 22:09:14 +04:00
Path.Join(Environment.CurrentDirectory, "modules")
});
// Also lookup load directories from config.
modulesDirectory.AddRange(_configuration.GetSection("ModuleDirectories").GetChildren().Select(q => q.Value)
.ToList());
return modulesDirectory;
}
}
2020-03-01 22:46:07 +04:00
2020-03-01 22:09:14 +04:00
#endregion
public Core(IConfiguration configuration)
{
_configuration = configuration;
2020-03-02 00:00:44 +04:00
HookAppDomain();
2020-03-01 22:09:14 +04:00
Start();
}
/// <summary>
/// Initializes all required submodules.
/// </summary>
private void Start()
{
var types = new List<Type>();
foreach (var directory in _directories)
{
if (!Directory.Exists(directory))
{
continue;
}
foreach (var file in Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories))
{
var assemblyTypes = LoadAssembly(file);
if (assemblyTypes == null)
{
continue;
}
types.AddRange(assemblyTypes);
}
}
2020-03-01 22:46:07 +04:00
// Instantiate them.
foreach (var module in types)
{
EnsureLoaded(module);
}
2020-03-01 22:09:14 +04:00
}
/// <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>
2022-01-22 17:13:38 +03:00
private IEnumerable<Type>? LoadAssembly(string path)
2020-03-01 22:09:14 +04:00
{
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)
{
2022-01-22 17:13:38 +03:00
module = (Module) Activator.CreateInstance(moduleType, this)!;
2020-03-02 08:49:57 +04:00
Console.WriteLine($"Started module {module.GetType().Name}");
2020-03-01 22:09:14 +04:00
_modules.Add(module);
}
return module;
}
2020-03-02 00:00:44 +04:00
private void HookAppDomain()
{
var appDomain = AppDomain.CurrentDomain;
2020-03-03 14:11:51 +04:00
appDomain.AssemblyResolve += delegate(object? sender, ResolveEventArgs args)
2020-03-02 00:00:44 +04:00
{
var assemblyName = new AssemblyName(args.Name).Name + ".dll";
var possiblePaths = new List<string>();
if (args.RequestingAssembly != null)
{
2022-01-22 17:13:38 +03:00
var modulePath = Path.GetDirectoryName(args.RequestingAssembly.Location)!;
possiblePaths.Add(modulePath);
possiblePaths.Add(args.RequestingAssembly.Location.Replace(".dll", ""));
}
2022-01-22 17:13:38 +03:00
possiblePaths.Add(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!);
possiblePaths.Add(Environment.CurrentDirectory);
foreach (var basePath in possiblePaths)
2020-03-02 00:00:44 +04:00
{
var assemblyPath = $"{basePath}/{assemblyName}";
if (File.Exists(assemblyPath))
{
return Assembly.LoadFrom(assemblyPath);
}
2020-03-02 00:00:44 +04:00
}
return null;
2020-03-02 00:00:44 +04:00
};
}
2020-03-01 22:09:14 +04:00
}
}